Merge branch 'main' of github.com:ctrliq/warewulf into main
This commit is contained in:
6
Makefile
6
Makefile
@@ -40,7 +40,7 @@ lint:
|
||||
@echo Running golangci-lint...
|
||||
@$(GOLANGCI_LINT) run --build-tags "$(WW_BUILD_GO_BUILD_TAGS)" ./...
|
||||
|
||||
all: vendor warewulfd wwctl wwclient
|
||||
all: vendor wwctl wwclient
|
||||
|
||||
files: all
|
||||
install -d -m 0755 /var/warewulf/
|
||||
@@ -60,9 +60,6 @@ services: files
|
||||
# sudo systemctl enable tftp
|
||||
# sudo systemctl restart tftp
|
||||
|
||||
warewulfd:
|
||||
cd cmd/warewulfd; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../warewulfd
|
||||
|
||||
wwctl:
|
||||
cd cmd/wwctl; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../wwctl
|
||||
|
||||
@@ -70,7 +67,6 @@ wwclient:
|
||||
cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags '-extldflags -static' -o ../../wwclient
|
||||
|
||||
clean:
|
||||
rm -f warewulfd
|
||||
rm -f wwclient
|
||||
rm -f wwctl
|
||||
|
||||
|
||||
65
README.md
65
README.md
@@ -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 build centos-7
|
||||
sudo ./wwctl kernel build $(uname -r)
|
||||
sudo ./wwctl container pull docker://warewulf/centos-7 centos-7 --setdefault
|
||||
sudo ./wwctl kernel build $(uname -r) --setdefault
|
||||
```
|
||||
|
||||
#### 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
|
||||
```
|
||||
|
||||
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 list
|
||||
```
|
||||
|
||||
#### 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
@@ -125,7 +179,8 @@ and then begin booting nodes.
|
||||
|
||||
```
|
||||
sudo ./wwctl ready
|
||||
./warewulfd
|
||||
sudo ./wwctl server start
|
||||
sudo ./wwctl server status
|
||||
```
|
||||
|
||||
#### Boot your compute node and watch it boot
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/app/warewulfd"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
root := warewulfd.GetRootCommand()
|
||||
|
||||
root.Execute()
|
||||
}
|
||||
12
etc/ipxe/unconfigured.ipxe
Normal file
12
etc/ipxe/unconfigured.ipxe
Normal 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
|
||||
@@ -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
1
go.mod
@@ -42,6 +42,7 @@ require (
|
||||
github.com/prometheus/common v0.6.0 // indirect
|
||||
github.com/prometheus/procfs v0.0.3 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/sevlyar/go-daemon v0.1.5 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -201,6 +201,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
@@ -340,6 +342,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk=
|
||||
github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package service
|
||||
package configure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/service/dhcp"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/service/tftp"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/configure/dhcp"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/configure/tftp"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
baseCmd = &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "Initialize Warewulf services",
|
||||
Use: "configure",
|
||||
Short: "Configure Warewulf services",
|
||||
Long: "Warewulf Service Initialization",
|
||||
RunE: CobraRunE,
|
||||
}
|
||||
@@ -24,7 +24,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
container.Build(c, BuildForce)
|
||||
output, err := container.Build(c, BuildForce)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", c, err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("%-20s: %s\n", c, output)
|
||||
}
|
||||
}
|
||||
|
||||
if SetDefault == true {
|
||||
|
||||
44
internal/app/wwctl/container/delete/main.go
Normal file
44
internal/app/wwctl/container/delete/main.go
Normal 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
|
||||
}
|
||||
25
internal/app/wwctl/container/delete/root.go
Normal file
25
internal/app/wwctl/container/delete/root.go
Normal 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
|
||||
}
|
||||
@@ -4,10 +4,11 @@ import "github.com/spf13/cobra"
|
||||
|
||||
var (
|
||||
baseCmd = &cobra.Command{
|
||||
Use: "__child",
|
||||
Hidden: true,
|
||||
RunE: CobraRunE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Use: "__child",
|
||||
Hidden: true,
|
||||
RunE: CobraRunE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -36,5 +36,14 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Rebuilding container...\n")
|
||||
output, err := container.Build(containerName, false)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", containerName, err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("%s\n", output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
|
||||
var (
|
||||
baseCmd = &cobra.Command{
|
||||
Use: "exec",
|
||||
Short: "Spawn any command inside a Warewulf container",
|
||||
Long: "Run a command inside a Warewulf container ",
|
||||
RunE: CobraRunE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Use: "exec",
|
||||
Short: "Spawn any command inside a Warewulf container",
|
||||
Long: "Run a command inside a Warewulf container ",
|
||||
RunE: CobraRunE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
nconfig, _ := node.New()
|
||||
nodes, _ := nconfig.FindAllNodes()
|
||||
nodeDB, _ := node.New()
|
||||
nodes, _ := nodeDB.FindAllNodes()
|
||||
nodemap := make(map[string]int)
|
||||
|
||||
for _, n := range nodes {
|
||||
|
||||
@@ -3,6 +3,7 @@ package pull
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -50,9 +51,35 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if SetBuild == true {
|
||||
fmt.Printf("Building container: %s\n", name)
|
||||
container.Build(name, false)
|
||||
fmt.Printf("Building container: %s\n", name)
|
||||
output, err := container.Build(name, true)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", name, err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("%s: %s\n", name, output)
|
||||
}
|
||||
|
||||
if SetDefault == true {
|
||||
nodeDB, err := node.New()
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map
|
||||
profiles, _ := nodeDB.FindAllProfiles()
|
||||
for _, profile := range profiles {
|
||||
wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get())
|
||||
if profile.Id.Get() == "default" {
|
||||
wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", name)
|
||||
profile.ContainerName.Set(name)
|
||||
nodeDB.ProfileUpdate(profile)
|
||||
}
|
||||
}
|
||||
nodeDB.Persist()
|
||||
fmt.Printf("Set default profile to container: %s\n", name)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -10,15 +10,17 @@ var (
|
||||
RunE: CobraRunE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
SetForce bool
|
||||
SetUpdate bool
|
||||
SetBuild bool
|
||||
SetForce bool
|
||||
SetUpdate bool
|
||||
SetBuild bool
|
||||
SetDefault bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force overwrite of an existing container")
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetUpdate, "update", "u", false, "Update and overwrite an existing container")
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetBuild, "build", "b", false, "Build container when after pulling")
|
||||
baseCmd.PersistentFlags().BoolVar(&SetDefault, "setdefault", false, "Set this container for the default profile")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package container
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/container/build"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/container/delete"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/container/exec"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/container/list"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/container/pull"
|
||||
@@ -22,7 +23,7 @@ func init() {
|
||||
baseCmd.AddCommand(list.GetCommand())
|
||||
baseCmd.AddCommand(pull.GetCommand())
|
||||
baseCmd.AddCommand(exec.GetCommand())
|
||||
|
||||
baseCmd.AddCommand(delete.GetCommand())
|
||||
}
|
||||
|
||||
// GetRootCommand returns the root cobra.Command for the application.
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
|
||||
for _, arg := range args {
|
||||
err := kernel.Build(arg)
|
||||
output, err := kernel.Build(arg)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Failed building kernel: %s\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("%s: %s\n", arg, output)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -10,13 +10,14 @@ var (
|
||||
RunE: CobraRunE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
SetGroup string
|
||||
SetController string
|
||||
SetNetDev string
|
||||
SetIpaddr string
|
||||
SetNetmask string
|
||||
SetGateway string
|
||||
SetHwaddr string
|
||||
SetGroup string
|
||||
SetController string
|
||||
SetNetDev string
|
||||
SetIpaddr string
|
||||
SetNetmask string
|
||||
SetGateway string
|
||||
SetHwaddr string
|
||||
SetDiscoverable bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -27,6 +28,7 @@ func init() {
|
||||
baseCmd.PersistentFlags().StringVarP(&SetNetmask, "netmask", "M", "", "Set the node's network device netmask")
|
||||
baseCmd.PersistentFlags().StringVarP(&SetGateway, "gateway", "G", "", "Set the node's network device gateway")
|
||||
baseCmd.PersistentFlags().StringVarP(&SetHwaddr, "hwaddr", "H", "", "Set the node's network device HW address")
|
||||
baseCmd.PersistentFlags().BoolVar(&SetDiscoverable, "discoverable", false, "Make this node discoverable")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "ClusterName", node.ClusterName.Source(), node.ClusterName.Print())
|
||||
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Profiles", "--", strings.Join(node.Profiles, ","))
|
||||
|
||||
fmt.Printf("%-20s %-18s %-12s %t\n", node.Id.Get(), "Disabled", node.Disabled.Source(), node.Disabled.PrintB())
|
||||
fmt.Printf("%-20s %-18s %-12s %t\n", node.Id.Get(), "Discoverable", node.Discoverable.Source(), node.Discoverable.PrintB())
|
||||
|
||||
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "ContainerName", node.ContainerName.Source(), node.ContainerName.Print())
|
||||
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelVersion", node.KernelVersion.Source(), node.KernelVersion.Print())
|
||||
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelArgs", node.KernelArgs.Source(), node.KernelArgs.Print())
|
||||
|
||||
@@ -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 {
|
||||
for _, p := range SetAddProfile {
|
||||
wwlog.Printf(wwlog.VERBOSE, "Node: %s, adding profile to '%s'\n", n.Id.Get(), p)
|
||||
|
||||
@@ -33,6 +33,8 @@ var (
|
||||
SetDelProfile []string
|
||||
SetForce bool
|
||||
SetInit string
|
||||
SetDiscoverable bool
|
||||
SetUndiscoverable bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -63,6 +65,8 @@ func init() {
|
||||
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked")
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force configuration (even on error)")
|
||||
baseCmd.PersistentFlags().BoolVar(&SetDiscoverable, "discoverable", false, "Make this node discoverable")
|
||||
baseCmd.PersistentFlags().BoolVar(&SetUndiscoverable, "undiscoverable", false, "Remove the discoverable flag")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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, "# of macros and logic which can be used within this file:\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, "# Network Config = {{.NetDevs.eth0.Ipaddr}}, {{.NetDevs.eth0.Hwaddr}}, etc.\n")
|
||||
fmt.Fprintf(w, "#\n")
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package wwctl
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/configure"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/container"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/kernel"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/node"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/overlay"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/profile"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/ready"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/service"
|
||||
"github.com/hpcng/warewulf/internal/app/wwctl/server"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -37,8 +38,9 @@ func init() {
|
||||
rootCmd.AddCommand(kernel.GetCommand())
|
||||
// rootCmd.AddCommand(group.GetCommand())
|
||||
rootCmd.AddCommand(profile.GetCommand())
|
||||
rootCmd.AddCommand(service.GetCommand())
|
||||
rootCmd.AddCommand(configure.GetCommand())
|
||||
rootCmd.AddCommand(ready.GetCommand())
|
||||
rootCmd.AddCommand(server.GetCommand())
|
||||
|
||||
}
|
||||
|
||||
|
||||
29
internal/app/wwctl/server/root.go
Normal file
29
internal/app/wwctl/server/root.go
Normal 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
|
||||
}
|
||||
11
internal/app/wwctl/server/start/main.go
Normal file
11
internal/app/wwctl/server/start/main.go
Normal 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()
|
||||
}
|
||||
20
internal/app/wwctl/server/start/root.go
Normal file
20
internal/app/wwctl/server/start/root.go
Normal 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
|
||||
}
|
||||
11
internal/app/wwctl/server/status/main.go
Normal file
11
internal/app/wwctl/server/status/main.go
Normal 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
|
||||
}
|
||||
20
internal/app/wwctl/server/status/root.go
Normal file
20
internal/app/wwctl/server/status/root.go
Normal 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
|
||||
}
|
||||
11
internal/app/wwctl/server/stop/main.go
Normal file
11
internal/app/wwctl/server/stop/main.go
Normal 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
|
||||
}
|
||||
20
internal/app/wwctl/server/stop/root.go
Normal file
20
internal/app/wwctl/server/stop/root.go
Normal 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
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hpcng/warewulf/internal/pkg/errors"
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"os"
|
||||
@@ -9,46 +10,40 @@ import (
|
||||
"path"
|
||||
)
|
||||
|
||||
func Build(name string, buildForce bool) {
|
||||
func Build(name string, buildForce bool) (string, error) {
|
||||
|
||||
rootfsPath := RootFsDir(name)
|
||||
imagePath := ImageFile(name)
|
||||
|
||||
if ValidSource(name) == false {
|
||||
wwlog.Printf(wwlog.INFO, "%-35s: Skipping (bad path)\n", name)
|
||||
return
|
||||
return "", errors.New("Container does not exist")
|
||||
}
|
||||
|
||||
if buildForce == false {
|
||||
wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n")
|
||||
if util.PathIsNewer(rootfsPath, imagePath) {
|
||||
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", name)
|
||||
return
|
||||
return "Skipping (VNFS is current)", nil
|
||||
}
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", name)
|
||||
err := os.MkdirAll(path.Dir(imagePath), 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %s\n", err)
|
||||
return
|
||||
return "Failed creating directory", err
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", rootfsPath)
|
||||
err = os.MkdirAll(path.Dir(rootfsPath), 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %s\n", err)
|
||||
return
|
||||
return "Failed creating directory", err
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", rootfsPath, imagePath)
|
||||
cmd := fmt.Sprintf("cd %s; find . | cpio --quiet -o -H newc | gzip -c > \"%s\"", rootfsPath, imagePath)
|
||||
err = exec.Command("/bin/sh", "-c", cmd).Run()
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Failed building VNFS: %s\n", err)
|
||||
return
|
||||
return "Failed building VNFS", err
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", name)
|
||||
|
||||
return "Done", nil
|
||||
}
|
||||
|
||||
@@ -77,9 +77,15 @@ func ValidSource(name string) bool {
|
||||
}
|
||||
|
||||
if util.IsDir(fullPath) == false {
|
||||
wwlog.Printf(wwlog.WARN, "Location is not a VNFS source directory: %s\n", name)
|
||||
wwlog.Printf(wwlog.VERBOSE, "Location is not a VNFS source directory: %s\n", name)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func DeleteSource(name string) error {
|
||||
fullPath := SourceDir(name)
|
||||
|
||||
return os.RemoveAll(fullPath)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func ListKernels() ([]string, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Build(kernelVersion string) error {
|
||||
func Build(kernelVersion string) (string, error) {
|
||||
|
||||
kernelImage := "/boot/vmlinuz-" + kernelVersion
|
||||
kernelDrivers := "/lib/modules/" + kernelVersion
|
||||
@@ -81,20 +81,19 @@ func Build(kernelVersion string) error {
|
||||
os.MkdirAll(path.Dir(driversDestination), 0755)
|
||||
|
||||
if util.IsFile(kernelImage) == false {
|
||||
return errors.New("Could not locate kernel image: " + kernelImage)
|
||||
return "", errors.New("Could not locate kernel image")
|
||||
}
|
||||
|
||||
if util.IsDir(kernelDrivers) == false {
|
||||
return errors.New("Could not locate kernel drivers: " + kernelDrivers)
|
||||
return "", errors.New("Could not locate kernel drivers")
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.VERBOSE, "Setting up Kernel\n")
|
||||
if _, err := os.Stat(kernelImage); err == nil {
|
||||
err := util.CopyFile(kernelImage, kernelDestination)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
wwlog.Printf(wwlog.INFO, "%-45s: Done\n", "vmlinuz-"+kernelVersion)
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.VERBOSE, "Building Kernel driver image\n")
|
||||
@@ -102,10 +101,9 @@ func Build(kernelVersion string) error {
|
||||
cmd := fmt.Sprintf("cd /; find .%s | cpio --quiet -o -H newc -F \"%s\"", kernelDrivers, driversDestination)
|
||||
err := exec.Command("/bin/sh", "-c", cmd).Run()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
wwlog.Printf(wwlog.INFO, "%-45s: Done\n", "kmods-"+kernelVersion+".img")
|
||||
}
|
||||
|
||||
return nil
|
||||
return "Done", nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -74,6 +75,9 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
|
||||
n.SystemOverlay.Set(node.SystemOverlay)
|
||||
n.RuntimeOverlay.Set(node.RuntimeOverlay)
|
||||
|
||||
n.Discoverable.SetB(node.Discoverable)
|
||||
n.Disabled.SetB(node.Disabled)
|
||||
|
||||
for devname, netdev := range node.NetDevs {
|
||||
if _, ok := n.NetDevs[devname]; !ok {
|
||||
var netdev NetDevEntry
|
||||
@@ -113,6 +117,9 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
|
||||
n.SystemOverlay.SetAlt(self.NodeProfiles[p].SystemOverlay, pstring)
|
||||
n.RuntimeOverlay.SetAlt(self.NodeProfiles[p].RuntimeOverlay, pstring)
|
||||
|
||||
n.Disabled.SetAltB(self.NodeProfiles[p].Disabled, pstring)
|
||||
n.Discoverable.SetAltB(self.NodeProfiles[p].Discoverable, pstring)
|
||||
|
||||
for devname, netdev := range self.NodeProfiles[p].NetDevs {
|
||||
if _, ok := n.NetDevs[devname]; !ok {
|
||||
var netdev NetDevEntry
|
||||
@@ -157,6 +164,9 @@ func (self *nodeYaml) FindAllProfiles() ([]NodeInfo, error) {
|
||||
p.RuntimeOverlay.Set(profile.RuntimeOverlay)
|
||||
p.SystemOverlay.Set(profile.SystemOverlay)
|
||||
|
||||
p.Disabled.SetB(profile.Disabled)
|
||||
p.Discoverable.SetB(profile.Discoverable)
|
||||
|
||||
for devname, netdev := range profile.NetDevs {
|
||||
if _, ok := p.NetDevs[devname]; !ok {
|
||||
var netdev NetDevEntry
|
||||
@@ -177,9 +187,40 @@ func (self *nodeYaml) FindAllProfiles() ([]NodeInfo, error) {
|
||||
|
||||
ret = append(ret, p)
|
||||
}
|
||||
|
||||
sort.Slice(ret, func(i, j int) bool {
|
||||
if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() {
|
||||
return true
|
||||
} else if ret[i].ClusterName.Get() == ret[j].ClusterName.Get() {
|
||||
if ret[i].Id.Get() < ret[j].Id.Get() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *nodeYaml) FindDiscoverableNode() (NodeInfo, string, error) {
|
||||
var ret NodeInfo
|
||||
|
||||
nodes, _ := self.FindAllNodes()
|
||||
|
||||
for _, node := range nodes {
|
||||
if node.Discoverable.GetB() == false {
|
||||
continue
|
||||
}
|
||||
for netdev, dev := range node.NetDevs {
|
||||
if dev.Hwaddr.Defined() == false {
|
||||
return node, netdev, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, "", errors.New("No unconfigured nodes found")
|
||||
}
|
||||
|
||||
func (self *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) {
|
||||
var ret NodeInfo
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"os"
|
||||
@@ -31,6 +32,7 @@ type NodeConf struct {
|
||||
RuntimeOverlay string `yaml:"runtime overlay files,omitempty"`
|
||||
SystemOverlay string `yaml:"system overlay files,omitempty"`
|
||||
Init string `yaml:"init,omitempty"`
|
||||
Discoverable bool `yaml:"discoverable,omitempty"`
|
||||
Profiles []string `yaml:"profiles,omitempty"`
|
||||
NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"`
|
||||
}
|
||||
@@ -73,6 +75,8 @@ type NodeInfo struct {
|
||||
IpmiPassword Entry
|
||||
RuntimeOverlay Entry
|
||||
SystemOverlay Entry
|
||||
Discoverable Entry
|
||||
Disabled Entry
|
||||
Init Entry //TODO: Finish adding this...
|
||||
Profiles []string
|
||||
GroupProfiles []string
|
||||
@@ -91,7 +95,20 @@ type NetDevEntry struct {
|
||||
func init() {
|
||||
//TODO: Check to make sure nodes.conf is found
|
||||
if util.IsFile(ConfigFile) == false {
|
||||
wwlog.Printf(wwlog.ERROR, "Configuration file not found: %s\n", ConfigFile)
|
||||
os.Exit(1)
|
||||
c, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "Could not create new configuration file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Fprintf(c, "nodeprofiles:\n")
|
||||
fmt.Fprintf(c, " default:\n")
|
||||
fmt.Fprintf(c, " comment: This profile is automatically included for each node\n")
|
||||
fmt.Fprintf(c, " kernel args: crashkernel=no quiet\n")
|
||||
fmt.Fprintf(c, "nodes: {}\n")
|
||||
|
||||
c.Close()
|
||||
|
||||
wwlog.Printf(wwlog.INFO, "Created default node configuration\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,10 @@ func (self *Entry) SetAlt(val string, from string) {
|
||||
}
|
||||
|
||||
func (self *Entry) SetAltB(val bool, from string) {
|
||||
self.altbool = val
|
||||
self.from = from
|
||||
if val == true {
|
||||
self.altbool = val
|
||||
self.from = from
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -106,6 +108,13 @@ func (self *Entry) Print() string {
|
||||
return "--"
|
||||
}
|
||||
|
||||
func (self *Entry) PrintB() bool {
|
||||
if self.from == "" {
|
||||
return self.bool
|
||||
}
|
||||
return self.altbool
|
||||
}
|
||||
|
||||
func (self *Entry) Source() string {
|
||||
if self.value != "" && self.altvalue != "" {
|
||||
return "SUPERSEDED"
|
||||
|
||||
@@ -67,6 +67,10 @@ func (self *nodeYaml) NodeUpdate(node NodeInfo) error {
|
||||
self.Nodes[nodeID].IpmiPassword = node.IpmiPassword.GetReal()
|
||||
self.Nodes[nodeID].RuntimeOverlay = node.RuntimeOverlay.GetReal()
|
||||
self.Nodes[nodeID].SystemOverlay = node.SystemOverlay.GetReal()
|
||||
|
||||
self.Nodes[nodeID].Disabled = node.Disabled.GetRealB()
|
||||
self.Nodes[nodeID].Discoverable = node.Discoverable.GetRealB()
|
||||
|
||||
self.Nodes[nodeID].Profiles = node.Profiles
|
||||
self.Nodes[nodeID].NetDevs = make(map[string]*NetDevs)
|
||||
|
||||
@@ -140,6 +144,10 @@ func (self *nodeYaml) ProfileUpdate(profile NodeInfo) error {
|
||||
self.NodeProfiles[profileID].IpmiPassword = profile.IpmiPassword.GetReal()
|
||||
self.NodeProfiles[profileID].RuntimeOverlay = profile.RuntimeOverlay.GetReal()
|
||||
self.NodeProfiles[profileID].SystemOverlay = profile.SystemOverlay.GetReal()
|
||||
|
||||
self.NodeProfiles[profileID].Disabled = profile.Disabled.GetRealB()
|
||||
self.NodeProfiles[profileID].Discoverable = profile.Discoverable.GetRealB()
|
||||
|
||||
self.NodeProfiles[profileID].Profiles = profile.Profiles
|
||||
self.NodeProfiles[profileID].NetDevs = make(map[string]*NetDevs)
|
||||
|
||||
|
||||
@@ -119,9 +119,13 @@ func buildOverlay(nodeList []node.NodeInfo, overlayType string) error {
|
||||
var OverlayFile string
|
||||
|
||||
if overlayType == "runtime" {
|
||||
wwlog.Printf(wwlog.VERBOSE, "Building runtime overlay for: %s\n", n.Id.Get())
|
||||
|
||||
OverlayDir = config.RuntimeOverlaySource(n.RuntimeOverlay.Get())
|
||||
OverlayFile = config.RuntimeOverlayImage(n.Id.Get())
|
||||
} else if overlayType == "system" {
|
||||
wwlog.Printf(wwlog.VERBOSE, "Building system overlay for: %s\n", n.Id.Get())
|
||||
|
||||
OverlayDir = config.SystemOverlaySource(n.RuntimeOverlay.Get())
|
||||
OverlayFile = config.SystemOverlayImage(n.Id.Get())
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package response
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||
114
internal/pkg/warewulfd/daemon.go
Normal file
114
internal/pkg/warewulfd/daemon.go
Normal 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
|
||||
}
|
||||
144
internal/pkg/warewulfd/ipxe.go
Normal file
144
internal/pkg/warewulfd/ipxe.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package response
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/pkg/kernel"
|
||||
@@ -1,4 +1,4 @@
|
||||
package response
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/pkg/kernel"
|
||||
@@ -1,4 +1,4 @@
|
||||
package response
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package response
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"github.com/hpcng/warewulf/internal/pkg/config"
|
||||
@@ -1,4 +1,4 @@
|
||||
package response
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
27
internal/pkg/warewulfd/warewulfd.go
Normal file
27
internal/pkg/warewulfd/warewulfd.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user