Merge branch 'refac/provision' of https://github.com/kcdodd/warewulf into kcdodd-refac/provision
This commit is contained in:
3
Makefile
3
Makefile
@@ -154,7 +154,7 @@ test-cover: ## Run test coverage and generate html report
|
|||||||
rm *.coverprofile
|
rm *.coverprofile
|
||||||
go tool cover -html=coverage/cover.out -o=coverage/cover.html
|
go tool cover -html=coverage/cover.out -o=coverage/cover.html
|
||||||
|
|
||||||
debian: all
|
debian: all
|
||||||
|
|
||||||
files: all
|
files: all
|
||||||
install -d -m 0755 $(DESTDIR)$(BINDIR)
|
install -d -m 0755 $(DESTDIR)$(BINDIR)
|
||||||
@@ -265,4 +265,3 @@ clean:
|
|||||||
install: files install_wwclient
|
install: files install_wwclient
|
||||||
|
|
||||||
debinstall: files debfiles
|
debinstall: files debfiles
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
#!ipxe
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo ================================================================================
|
|
||||||
echo Warewulf v4 now booting: {{.Fqdn}}
|
|
||||||
echo
|
|
||||||
echo Container: {{.ContainerName}}
|
|
||||||
{{if .KernelOverride }}
|
|
||||||
echo Kernel: {{.KernelOverride}}
|
|
||||||
{{else}}
|
|
||||||
echo Kernel: {{.ContainerName}} (container default)
|
|
||||||
{{end}}
|
|
||||||
echo KernelArgs: {{.KernelArgs}}
|
|
||||||
echo
|
|
||||||
|
|
||||||
set base http://{{.Ipaddr}}:{{.Port}}
|
|
||||||
|
|
||||||
kernel --name kernel ${base}/kernel/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
imgextract --name container ${base}/container/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
imgextract --name system ${base}/overlay-system/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
imgextract --name runtime ${base}/overlay-runtime/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
imgextract --name runtime ${base}/overlay-runtime/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
|
|
||||||
boot kernel initrd=container initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
|
||||||
|
|
||||||
:reboot
|
|
||||||
echo
|
|
||||||
echo There was an error, rebooting in 15s...
|
|
||||||
echo
|
|
||||||
sleep 15
|
|
||||||
reboot
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo ================================================================================
|
echo ================================================================================
|
||||||
echo Warewulf v4 now booting: {{.Fqdn}}
|
echo Warewulf v4 now booting: {{.Fqdn}} ({{.Hwaddr}})
|
||||||
echo
|
echo
|
||||||
echo Container: {{.ContainerName}}
|
echo Container: {{.ContainerName}}
|
||||||
{{if .KernelOverride }}
|
{{if .KernelOverride }}
|
||||||
@@ -13,14 +13,56 @@ echo Kernel: {{.ContainerName}} (container default)
|
|||||||
echo KernelArgs: {{.KernelArgs}}
|
echo KernelArgs: {{.KernelArgs}}
|
||||||
echo
|
echo
|
||||||
|
|
||||||
set base http://{{.Ipaddr}}:{{.Port}}
|
set uri_base http://{{.Ipaddr}}:{{.Port}}/provision/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid}
|
||||||
|
echo Downloading node provisions: ${uri_base}
|
||||||
|
|
||||||
kernel --name kernel ${base}/kernel/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
echo Kernel image:
|
||||||
initrd --name container ${base}/container/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
kernel --name kernel ${uri_base}&stage=kernel || goto reboot
|
||||||
initrd --name system ${base}/overlay-system/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
initrd --name runtime ${base}/overlay-runtime/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
|
||||||
|
|
||||||
boot kernel initrd=container initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
# try extracting compressed images first
|
||||||
|
# NOTE: system overlay tends to be the smallest, so failure here is the cheapest
|
||||||
|
echo System image:
|
||||||
|
imgextract --name system ${uri_base}&stage=system&compress=gz || goto nocompress
|
||||||
|
|
||||||
|
echo Container image:
|
||||||
|
imgextract --name container ${uri_base}&stage=container&compress=gz || goto reboot
|
||||||
|
|
||||||
|
{{if ne .KernelOverride "" -}}
|
||||||
|
echo Kernel modules image:
|
||||||
|
imgextract --name kmods ${uri_base}&stage=kmods&compress=gz || goto reboot
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
goto imoktogo
|
||||||
|
|
||||||
|
:nocompress
|
||||||
|
|
||||||
|
echo Extraction failed, attempting un-compressed version of images
|
||||||
|
|
||||||
|
echo System image:
|
||||||
|
initrd --name system ${uri_base}&stage=system || goto reboot
|
||||||
|
|
||||||
|
echo Container image:
|
||||||
|
initrd --name container ${uri_base}&stage=container || goto reboot
|
||||||
|
|
||||||
|
|
||||||
|
{{if ne .KernelOverride "" -}}
|
||||||
|
echo Kernel modules image:
|
||||||
|
initrd --name kmods ${uri_base}&stage=kmods || goto reboot
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
|
||||||
|
:imoktogo
|
||||||
|
|
||||||
|
echo Booting in 3s...
|
||||||
|
{{if ne .KernelOverride "" -}}
|
||||||
|
echo boot kernel initrd=container initrd=kmods initrd=system wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||||
|
sleep 3
|
||||||
|
boot kernel initrd=container initrd=kmods initrd=system wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||||
|
{{- else -}}
|
||||||
|
echo boot kernel initrd=container initrd=system wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||||
|
sleep 3
|
||||||
|
boot kernel initrd=container initrd=system wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
:reboot
|
:reboot
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ func updateSystem(ipaddr string, port int, wwid string, tag string, localUUID uu
|
|||||||
counter := 0
|
counter := 0
|
||||||
for {
|
for {
|
||||||
var err error
|
var err error
|
||||||
getString := fmt.Sprintf("http://%s:%d/overlay-runtime/%s?assetkey=%s&uuid=%s", ipaddr, port, wwid, tag, localUUID)
|
getString := fmt.Sprintf("http://%s:%d/provision/%s?assetkey=%s&uuid=%s&stage=runtime&compress=gz", ipaddr, port, wwid, tag, localUUID)
|
||||||
wwlog.Printf(wwlog.DEBUG, "Making request: %s\n", getString)
|
wwlog.Printf(wwlog.DEBUG, "Making request: %s\n", getString)
|
||||||
resp, err = Webclient.Get(getString)
|
resp, err = Webclient.Get(getString)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
name = args[1]
|
name = args[1]
|
||||||
} else {
|
} else {
|
||||||
name = path.Base(uri)
|
name = path.Base(uri)
|
||||||
fmt.Printf("Setting VNFS name: %s\n", name)
|
wwlog.Info("Setting VNFS name: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !container.ValidName(name) {
|
if !container.ValidName(name) {
|
||||||
wwlog.Printf(wwlog.ERROR, "VNFS name contains illegal characters: %s\n", name)
|
wwlog.Error("VNFS name contains illegal characters: %s", name)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,75 +87,75 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if util.IsDir(fullPath) {
|
if util.IsDir(fullPath) {
|
||||||
if SetForce {
|
if SetForce {
|
||||||
fmt.Printf("Overwriting existing VNFS\n")
|
wwlog.Info("Overwriting existing VNFS")
|
||||||
err := os.RemoveAll(fullPath)
|
err := os.RemoveAll(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else if SetUpdate {
|
} else if SetUpdate {
|
||||||
fmt.Printf("Updating existing VNFS\n")
|
wwlog.Info("Updating existing VNFS")
|
||||||
} else {
|
} else {
|
||||||
wwlog.Printf(wwlog.ERROR, "VNFS Name exists, specify --force, --update, or choose a different name: %s\n", name)
|
wwlog.Error("VNFS Name exists, specify --force, --update, or choose a different name: %s", name)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(uri, "docker://") || strings.HasPrefix(uri, "docker-daemon://") ||
|
} else if strings.HasPrefix(uri, "docker://") || strings.HasPrefix(uri, "docker-daemon://") ||
|
||||||
strings.HasPrefix(uri, "file://") || util.IsFile(uri) {
|
strings.HasPrefix(uri, "file://") || util.IsFile(uri) {
|
||||||
sCtx, err := getSystemContext()
|
sCtx, err := getSystemContext()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = container.ImportDocker(uri, name, sCtx)
|
err = container.ImportDocker(uri, name, sCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not import image: %s\n", err)
|
wwlog.Error("Could not import image: %s", err)
|
||||||
_ = container.DeleteSource(name)
|
_ = container.DeleteSource(name)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else if util.IsDir(uri) {
|
} else if util.IsDir(uri) {
|
||||||
err := container.ImportDirectory(uri, name)
|
err := container.ImportDirectory(uri, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not import image: %s\n", err)
|
wwlog.Error("Could not import image: %s", err)
|
||||||
_ = container.DeleteSource(name)
|
_ = container.DeleteSource(name)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wwlog.Printf(wwlog.ERROR, "Invalid dir or uri: %s\n", uri)
|
wwlog.Error("Invalid dir or uri: %s", uri)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Updating the container's /etc/resolv.conf\n")
|
wwlog.Info("Updating the container's /etc/resolv.conf")
|
||||||
err := util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(name), "/etc/resolv.conf"))
|
err := util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(name), "/etc/resolv.conf"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.WARN, "Could not copy /etc/resolv.conf into container: %s\n", err)
|
wwlog.Warn("Could not copy /etc/resolv.conf into container: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = container.SyncUids(name, !SyncUser)
|
err = container.SyncUids(name, !SyncUser)
|
||||||
if err != nil && !SyncUser {
|
if err != nil && !SyncUser {
|
||||||
wwlog.Printf(wwlog.ERROR, "Error in user sync, fix error and run 'syncuser' manually: %s\n", err)
|
wwlog.Error("Error in user sync, fix error and run 'syncuser' manually: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Building container: %s\n", name)
|
wwlog.Info("Building container: %s", name)
|
||||||
err = container.Build(name, true)
|
err = container.Build(name, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", name, err)
|
wwlog.Error("Could not build container %s: %s", name, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if SetDefault {
|
if SetDefault {
|
||||||
nodeDB, err := node.New()
|
nodeDB, err := node.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err)
|
wwlog.Error("Could not open node configuration: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map
|
//TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map
|
||||||
profiles, _ := nodeDB.FindAllProfiles()
|
profiles, _ := nodeDB.FindAllProfiles()
|
||||||
for _, profile := range profiles {
|
for _, profile := range profiles {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get())
|
wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s", profile.Id.Get())
|
||||||
if profile.Id.Get() == "default" {
|
if profile.Id.Get() == "default" {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", name)
|
wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s", name)
|
||||||
profile.ContainerName.Set(name)
|
profile.ContainerName.Set(name)
|
||||||
err := nodeDB.ProfileUpdate(profile)
|
err := nodeDB.ProfileUpdate(profile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -168,7 +168,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
return errors.Wrap(err, "failed to persist nodedb")
|
return errors.Wrap(err, "failed to persist nodedb")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Set default profile to container: %s\n", name)
|
wwlog.Info("Set default profile to container: %s", name)
|
||||||
err = warewulfd.DaemonReload()
|
err = warewulfd.DaemonReload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to reload warewulf daemon")
|
return errors.Wrap(err, "failed to reload warewulf daemon")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package add
|
package add
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||||
@@ -135,7 +134,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
// just check if address is a valid ipv6 CIDR address
|
// just check if address is a valid ipv6 CIDR address
|
||||||
if _, _, err := net.ParseCIDR(SetIpaddr6); err != nil {
|
if _, _, err := net.ParseCIDR(SetIpaddr6); err != nil {
|
||||||
return errors.New(fmt.Sprintf("%s is not a valid ipv6 address in CIDR notation\n", SetIpaddr6))
|
return errors.Errorf("%s is not a valid ipv6 address in CIDR notation\n", SetIpaddr6)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ func GetRootCommand() *cobra.Command {
|
|||||||
|
|
||||||
func rootPersistentPreRunE(cmd *cobra.Command, args []string) error {
|
func rootPersistentPreRunE(cmd *cobra.Command, args []string) error {
|
||||||
if DebugFlag {
|
if DebugFlag {
|
||||||
wwlog.SetLevel(wwlog.DEBUG)
|
wwlog.SetLogLevel(wwlog.DEBUG)
|
||||||
} else if verboseArg {
|
} else if verboseArg {
|
||||||
wwlog.SetLevel(wwlog.VERBOSE)
|
wwlog.SetLogLevel(wwlog.VERBOSE)
|
||||||
} else {
|
} else {
|
||||||
wwlog.SetLevel(wwlog.INFO)
|
wwlog.SetLogLevel(wwlog.INFO)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
@@ -18,50 +16,42 @@ func Build(name string, buildForce bool) error {
|
|||||||
imagePath := ImageFile(name)
|
imagePath := ImageFile(name)
|
||||||
|
|
||||||
if !ValidSource(name) {
|
if !ValidSource(name) {
|
||||||
return errors.New("Container does not exist")
|
return errors.Errorf("Container does not exist: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !buildForce {
|
if !buildForce {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n")
|
wwlog.Debug("Checking if there have been any updates to the VNFS directory")
|
||||||
if util.PathIsNewer(rootfsPath, imagePath) {
|
if util.PathIsNewer(rootfsPath, imagePath) {
|
||||||
wwlog.Printf(wwlog.INFO, "Skipping (VNFS is current)\n")
|
wwlog.Info("Skipping (VNFS is current)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", name)
|
excludes_file := path.Join(rootfsPath, "./etc/warewulf/excludes")
|
||||||
err := os.MkdirAll(path.Dir(imagePath), 0755)
|
ignore := []string{}
|
||||||
if err != nil {
|
|
||||||
return errors.New("Failed creating directory")
|
if util.IsFile(excludes_file) {
|
||||||
|
ignore, err := util.ReadFile(excludes_file)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed creating directory: %s", imagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, pattern := range ignore {
|
||||||
|
if ( strings.HasPrefix(pattern, "/") ) {
|
||||||
|
ignore[i] = pattern[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", rootfsPath)
|
err := util.BuildFsImage(
|
||||||
err = os.MkdirAll(path.Dir(rootfsPath), 0755)
|
"VNFS container " + name,
|
||||||
if err != nil {
|
rootfsPath,
|
||||||
return errors.New("Failed creating directory")
|
imagePath,
|
||||||
}
|
[]string{"*"},
|
||||||
|
ignore,
|
||||||
|
// ignore cross-device files
|
||||||
|
true,
|
||||||
|
"newc")
|
||||||
|
|
||||||
compressor, err := exec.LookPath("pigz")
|
return err
|
||||||
if err != nil {
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Could not locate PIGZ, using GZIP\n")
|
|
||||||
compressor = "gzip"
|
|
||||||
} else {
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Using PIGZ to compress the container: %s\n", compressor)
|
|
||||||
}
|
|
||||||
var cmd string
|
|
||||||
_, err = os.Stat(path.Join(rootfsPath, "./etc/warewulf/excludes"))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", rootfsPath, imagePath)
|
|
||||||
cmd = fmt.Sprintf("cd %s; find . -xdev -xautofs | cpio --quiet -o -H newc | %s -c > \"%s\"", rootfsPath, compressor, imagePath)
|
|
||||||
} else {
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Building VNFS image with excludes: '%s' -> '%s'\n", rootfsPath, imagePath)
|
|
||||||
cmd = fmt.Sprintf("cd %s; find . -xdev -xautofs | grep -v -f ./etc/warewulf/excludes | cpio --quiet -o -H newc | %s -c > \"%s\"", rootfsPath, compressor, imagePath)
|
|
||||||
}
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd)
|
|
||||||
err = exec.Command("/bin/sh", "-c", cmd).Run()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Failed building VNFS")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,5 +23,5 @@ func ImageParentDir() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImageFile(name string) string {
|
func ImageFile(name string) string {
|
||||||
return path.Join(ImageParentDir(), name+".img.gz")
|
return path.Join(ImageParentDir(), name+".img")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ var (
|
|||||||
kernelNames = []string{
|
kernelNames = []string{
|
||||||
`vmlinux`,
|
`vmlinux`,
|
||||||
`vmlinuz`,
|
`vmlinuz`,
|
||||||
`vmlinuz.gz`,
|
`vmlinux-*`,
|
||||||
}
|
`vmlinuz-*`,
|
||||||
modulePath = "/lib/modules/"
|
`vmlinuz.gz` }
|
||||||
|
|
||||||
|
kernelDirs = []string{
|
||||||
|
`/lib/modules/*/`,
|
||||||
|
`/boot/` }
|
||||||
)
|
)
|
||||||
|
|
||||||
func KernelFind(container string) string {
|
func KernelFind(container string) string {
|
||||||
@@ -25,21 +29,28 @@ func KernelFind(container string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, kname := range kernelNames {
|
for _, kdir := range kernelDirs {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Checking for kernel name within module path: %s\n", kname)
|
wwlog.Printf(wwlog.DEBUG, "Checking kernel directory: %s\n", kdir)
|
||||||
kernelPaths, err := filepath.Glob(path.Join(container_path, modulePath, "/*/", kname))
|
for _, kname := range kernelNames {
|
||||||
if err != nil {
|
wwlog.Printf(wwlog.DEBUG, "Checking for kernel name: %s\n", kname)
|
||||||
return ""
|
kernelPaths, err := filepath.Glob(path.Join(container_path, kdir, kname))
|
||||||
}
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
sort.Slice(kernelPaths, func(i, j int) bool {
|
if len(kernelPaths) == 0 {
|
||||||
return kernelPaths[i] > kernelPaths[j]
|
continue
|
||||||
})
|
}
|
||||||
|
|
||||||
for _, kernelPath := range kernelPaths {
|
sort.Slice(kernelPaths, func(i, j int) bool {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Checking for kernel path: %s\n", kernelPath)
|
return kernelPaths[i] > kernelPaths[j]
|
||||||
if util.IsFile(kernelPath) {
|
})
|
||||||
return kernelPath
|
|
||||||
|
for _, kernelPath := range kernelPaths {
|
||||||
|
wwlog.Printf(wwlog.DEBUG, "Checking for kernel path: %s\n", kernelPath)
|
||||||
|
if util.IsFile(kernelPath) {
|
||||||
|
return kernelPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -35,12 +34,12 @@ func KernelImageTopDir() string {
|
|||||||
|
|
||||||
func KernelImage(kernelName string) string {
|
func KernelImage(kernelName string) string {
|
||||||
if kernelName == "" {
|
if kernelName == "" {
|
||||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
wwlog.Error("Kernel Name is not defined")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.ValidString(kernelName, "^[a-zA-Z0-9-._]+$") {
|
if !util.ValidString(kernelName, "^[a-zA-Z0-9-._]+$") {
|
||||||
wwlog.Printf(wwlog.ERROR, "Runtime overlay name contains illegal characters: %s\n", kernelName)
|
wwlog.Error("Runtime overlay name contains illegal characters: %s", kernelName)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ func KernelImage(kernelName string) string {
|
|||||||
|
|
||||||
func GetKernelVersion(kernelName string) string {
|
func GetKernelVersion(kernelName string) string {
|
||||||
if kernelName == "" {
|
if kernelName == "" {
|
||||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
wwlog.Error("Kernel Name is not defined")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
kernelVersion, err := ioutil.ReadFile(KernelVersionFile(kernelName))
|
kernelVersion, err := ioutil.ReadFile(KernelVersionFile(kernelName))
|
||||||
@@ -61,12 +60,12 @@ func GetKernelVersion(kernelName string) string {
|
|||||||
|
|
||||||
func KmodsImage(kernelName string) string {
|
func KmodsImage(kernelName string) string {
|
||||||
if kernelName == "" {
|
if kernelName == "" {
|
||||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
wwlog.Error("Kernel Name is not defined")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.ValidString(kernelName, "^[a-zA-Z0-9-._]+$") {
|
if !util.ValidString(kernelName, "^[a-zA-Z0-9-._]+$") {
|
||||||
wwlog.Printf(wwlog.ERROR, "Runtime overlay name contains illegal characters: %s\n", kernelName)
|
wwlog.Error("Runtime overlay name contains illegal characters: %s", kernelName)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +74,12 @@ func KmodsImage(kernelName string) string {
|
|||||||
|
|
||||||
func KernelVersionFile(kernelName string) string {
|
func KernelVersionFile(kernelName string) string {
|
||||||
if kernelName == "" {
|
if kernelName == "" {
|
||||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
wwlog.Error("Kernel Name is not defined")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.ValidString(kernelName, "^[a-zA-Z0-9-._]+$") {
|
if !util.ValidString(kernelName, "^[a-zA-Z0-9-._]+$") {
|
||||||
wwlog.Printf(wwlog.ERROR, "Runtime overlay name contains illegal characters: %s\n", kernelName)
|
wwlog.Error("Runtime overlay name contains illegal characters: %s", kernelName)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +94,7 @@ func ListKernels() ([]string, error) {
|
|||||||
return ret, errors.New("Could not create Kernel parent directory: " + KernelImageTopDir())
|
return ret, errors.New("Could not create Kernel parent directory: " + KernelImageTopDir())
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Searching for Kernel image directories: %s\n", KernelImageTopDir())
|
wwlog.Debug("Searching for Kernel image directories: %s", KernelImageTopDir())
|
||||||
|
|
||||||
kernels, err := ioutil.ReadDir(KernelImageTopDir())
|
kernels, err := ioutil.ReadDir(KernelImageTopDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,7 +102,7 @@ func ListKernels() ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, kernel := range kernels {
|
for _, kernel := range kernels {
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Found Kernel: %s\n", kernel.Name())
|
wwlog.Verbose("Found Kernel: %s", kernel.Name())
|
||||||
|
|
||||||
ret = append(ret, kernel.Name())
|
ret = append(ret, kernel.Name())
|
||||||
|
|
||||||
@@ -113,8 +112,8 @@ func ListKernels() ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Build(kernelVersion, kernelName, root string) (string, error) {
|
func Build(kernelVersion, kernelName, root string) (string, error) {
|
||||||
kernelDrivers := path.Join(root, "/lib/modules/", kernelVersion)
|
|
||||||
kernelDriversRelative := path.Join("/lib/modules/", kernelVersion)
|
kernelDriversRelative := path.Join("/lib/modules/", kernelVersion)
|
||||||
|
kernelDrivers := path.Join(root, kernelDriversRelative)
|
||||||
kernelDestination := KernelImage(kernelName)
|
kernelDestination := KernelImage(kernelName)
|
||||||
driversDestination := KmodsImage(kernelName)
|
driversDestination := KmodsImage(kernelName)
|
||||||
versionDestination := KernelVersionFile(kernelName)
|
versionDestination := KernelVersionFile(kernelName)
|
||||||
@@ -138,7 +137,7 @@ func Build(kernelVersion, kernelName, root string) (string, error) {
|
|||||||
|
|
||||||
for _, searchPath := range kernelSearchPaths {
|
for _, searchPath := range kernelSearchPaths {
|
||||||
testPath := fmt.Sprintf(path.Join(root, searchPath), kernelVersion)
|
testPath := fmt.Sprintf(path.Join(root, searchPath), kernelVersion)
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Looking for kernel at: %s\n", testPath)
|
wwlog.Verbose("Looking for kernel at: %s", testPath)
|
||||||
if util.IsFile(testPath) {
|
if util.IsFile(testPath) {
|
||||||
kernelSource = testPath
|
kernelSource = testPath
|
||||||
break
|
break
|
||||||
@@ -146,17 +145,17 @@ func Build(kernelVersion, kernelName, root string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kernelSource == "" {
|
if kernelSource == "" {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not locate kernel image\n")
|
wwlog.Error("Could not locate kernel image")
|
||||||
return "", errors.New("could not locate kernel image")
|
return "", errors.New("could not locate kernel image")
|
||||||
} else {
|
} else {
|
||||||
wwlog.Printf(wwlog.INFO, "Found kernel at: %s\n", kernelSource)
|
wwlog.Info("Found kernel at: %s", kernelSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.IsDir(kernelDrivers) {
|
if !util.IsDir(kernelDrivers) {
|
||||||
return "", errors.New("Could not locate kernel drivers")
|
return "", errors.New("Could not locate kernel drivers")
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Setting up Kernel\n")
|
wwlog.Verbose("Setting up Kernel")
|
||||||
if _, err := os.Stat(kernelSource); err == nil {
|
if _, err := os.Stat(kernelSource); err == nil {
|
||||||
kernel, err := os.Open(kernelSource)
|
kernel, err := os.Open(kernelSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -189,26 +188,30 @@ func Build(kernelVersion, kernelName, root string) (string, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Building Kernel driver image\n")
|
|
||||||
if _, err := os.Stat(kernelDrivers); err == nil {
|
if _, err := os.Stat(kernelDrivers); err == nil {
|
||||||
compressor, err := exec.LookPath("pigz")
|
name := kernelName + " drivers"
|
||||||
if err != nil {
|
wwlog.Verbose("Creating image for %s: %s", name, root)
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Could not locate PIGZ, using GZIP\n")
|
|
||||||
compressor = "gzip"
|
|
||||||
} else {
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Using PIGZ to compress the container: %s\n", compressor)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := fmt.Sprintf("cd %s; find .%s ./lib/firmware | cpio --quiet -o -L -H newc | %s -c > \"%s\"", root, kernelDriversRelative, compressor, driversDestination)
|
err = util.BuildFsImage(
|
||||||
|
name,
|
||||||
|
root,
|
||||||
|
driversDestination,
|
||||||
|
[]string{
|
||||||
|
"." + kernelDriversRelative,
|
||||||
|
"./lib/firmware" },
|
||||||
|
[]string{},
|
||||||
|
// ignore cross-device files
|
||||||
|
true,
|
||||||
|
"newc",
|
||||||
|
// dereference symbolic links
|
||||||
|
"-L")
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd)
|
|
||||||
err = exec.Command("/bin/sh", "-c", cmd).Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Creating version file\n")
|
wwlog.Verbose("Creating version file")
|
||||||
file, err := os.Create(versionDestination)
|
file, err := os.Create(versionDestination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "Failed to create version file")
|
return "", errors.Wrap(err, "Failed to create version file")
|
||||||
@@ -228,26 +231,26 @@ func Build(kernelVersion, kernelName, root string) (string, error) {
|
|||||||
func DeleteKernel(name string) error {
|
func DeleteKernel(name string) error {
|
||||||
fullPath := path.Join(KernelImageTopDir(), name)
|
fullPath := path.Join(KernelImageTopDir(), name)
|
||||||
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Removing path: %s\n", fullPath)
|
wwlog.Verbose("Removing path: %s", fullPath)
|
||||||
return os.RemoveAll(fullPath)
|
return os.RemoveAll(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindKernelVersion(root string) (string, error) {
|
func FindKernelVersion(root string) (string, error) {
|
||||||
for _, searchPath := range kernelSearchPaths {
|
for _, searchPath := range kernelSearchPaths {
|
||||||
testPattern := fmt.Sprintf(path.Join(root, searchPath), `*`)
|
testPattern := fmt.Sprintf(path.Join(root, searchPath), `*`)
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Looking for kernel version with pattern at: %s\n", testPattern)
|
wwlog.Verbose("Looking for kernel version with pattern at: %s", testPattern)
|
||||||
potentialKernel, _ := filepath.Glob(testPattern)
|
potentialKernel, _ := filepath.Glob(testPattern)
|
||||||
if len(potentialKernel) == 0 {
|
if len(potentialKernel) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, foundKernel := range potentialKernel {
|
for _, foundKernel := range potentialKernel {
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Parsing out kernel version for %s\n", foundKernel)
|
wwlog.Verbose("Parsing out kernel version for %s", foundKernel)
|
||||||
re := regexp.MustCompile(fmt.Sprintf(path.Join(root, searchPath), `([\w\d-\.]*)`))
|
re := regexp.MustCompile(fmt.Sprintf(path.Join(root, searchPath), `([\w\d-\.]*)`))
|
||||||
version := re.FindAllStringSubmatch(foundKernel, -1)
|
version := re.FindAllStringSubmatch(foundKernel, -1)
|
||||||
if version == nil {
|
if version == nil {
|
||||||
return "", fmt.Errorf("could not parse kernel version")
|
return "", fmt.Errorf("could not parse kernel version")
|
||||||
}
|
}
|
||||||
wwlog.Printf(wwlog.VERBOSE, "found kernel version %s\n", version)
|
wwlog.Verbose("found kernel version %s", version)
|
||||||
return version[0][1], nil
|
return version[0][1], nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ func templateFileBlock(inc string, abortStr string) (string, error) {
|
|||||||
}
|
}
|
||||||
cont += line + "\n"
|
cont += line + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: the text originally contains N-1 newlines for N lines, but the above
|
||||||
|
// loop will always add one at the end
|
||||||
|
// Avoids adding a blank line that was not present in the original file
|
||||||
|
// by adding 'abort' string to the end of the included block (without a newline)
|
||||||
|
// instead of manually in the template
|
||||||
|
cont += abortStr
|
||||||
|
|
||||||
return cont, nil
|
return cont, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -51,16 +50,16 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
|
|||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
|
|
||||||
sysOverlays := n.SystemOverlay.GetSlice()
|
sysOverlays := n.SystemOverlay.GetSlice()
|
||||||
wwlog.Printf(wwlog.INFO, "Building system overlays for %s: [%s]\n", n.Id.Get(), strings.Join(sysOverlays, ", "))
|
wwlog.Info("Building system overlays for %s: [%s]", n.Id.Get(), strings.Join(sysOverlays, ", "))
|
||||||
err := BuildOverlay(n, sysOverlays)
|
err := BuildOverlay(n, sysOverlays)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, fmt.Sprintf("could not build system overlays %v for nide %s\n", sysOverlays, n.Id.Get()))
|
return errors.Wrapf(err, "could not build system overlays %v for nide %s", sysOverlays, n.Id.Get())
|
||||||
}
|
}
|
||||||
runOverlays := n.RuntimeOverlay.GetSlice()
|
runOverlays := n.RuntimeOverlay.GetSlice()
|
||||||
wwlog.Printf(wwlog.INFO, "Building runtime overlays for %s: [%s]\n", n.Id.Get(), strings.Join(runOverlays, ", "))
|
wwlog.Info("Building runtime overlays for %s: [%s]", n.Id.Get(), strings.Join(runOverlays, ", "))
|
||||||
err = BuildOverlay(n, runOverlays)
|
err = BuildOverlay(n, runOverlays)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, fmt.Sprintf("could not build runtime overlays %v for nide %s\n", runOverlays, n.Id.Get()))
|
return errors.Wrapf(err, "could not build runtime overlays %v for nide %s", runOverlays, n.Id.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -72,7 +71,7 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
|
|||||||
func BuildSpecificOverlays(nodes []node.NodeInfo, overlayName string) error {
|
func BuildSpecificOverlays(nodes []node.NodeInfo, overlayName string) error {
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
|
|
||||||
wwlog.Printf(wwlog.INFO, "Building overlay for %s: %s\n", n.Id.Get(), overlayName)
|
wwlog.Info("Building overlay for %s: %s", n.Id.Get(), overlayName)
|
||||||
err := BuildOverlay(n, []string{overlayName})
|
err := BuildOverlay(n, []string{overlayName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not build overlay "+n.Id.Get()+"/"+overlayName+".img")
|
return errors.Wrap(err, "could not build overlay "+n.Id.Get()+"/"+overlayName+".img")
|
||||||
@@ -91,7 +90,7 @@ func BuildHostOverlay() error {
|
|||||||
host.Ipmi = new(node.IpmiEntry)
|
host.Ipmi = new(node.IpmiEntry)
|
||||||
var idEntry node.Entry
|
var idEntry node.Entry
|
||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
wwlog.Printf(wwlog.INFO, "Building overlay for %s: host\n", hostname)
|
wwlog.Info("Building overlay for %s: host", hostname)
|
||||||
idEntry.Set(hostname)
|
idEntry.Set(hostname)
|
||||||
host.Id = idEntry
|
host.Id = idEntry
|
||||||
hostdir := OverlaySourceDir("host")
|
hostdir := OverlaySourceDir("host")
|
||||||
@@ -100,7 +99,7 @@ func BuildHostOverlay() error {
|
|||||||
return errors.Wrap(err, "could not build host overlay ")
|
return errors.Wrap(err, "could not build host overlay ")
|
||||||
}
|
}
|
||||||
if !(stats.Mode() == os.FileMode(0750|os.ModeDir) || stats.Mode() == os.FileMode(0700|os.ModeDir)) {
|
if !(stats.Mode() == os.FileMode(0750|os.ModeDir) || stats.Mode() == os.FileMode(0700|os.ModeDir)) {
|
||||||
wwlog.Printf(wwlog.SECWARN, "Permissions of host overlay dir %s are %s (750 is considered as secure)\n", hostdir, stats.Mode())
|
wwlog.SecWarn("Permissions of host overlay dir %s are %s (750 is considered as secure)", hostdir, stats.Mode())
|
||||||
}
|
}
|
||||||
return BuildOverlayIndir(host, []string{"host"}, "/")
|
return BuildOverlayIndir(host, []string{"host"}, "/")
|
||||||
}
|
}
|
||||||
@@ -119,7 +118,7 @@ func FindOverlays() ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Evaluating overlay source: %s\n", file.Name())
|
wwlog.Debug("Evaluating overlay source: %s", file.Name())
|
||||||
isdotfile := dotfilecheck.MatchString(file.Name())
|
isdotfile := dotfilecheck.MatchString(file.Name())
|
||||||
|
|
||||||
if (file.IsDir()) && !(isdotfile) {
|
if (file.IsDir()) && !(isdotfile) {
|
||||||
@@ -150,47 +149,43 @@ Build the given overlays for a node and create a Image for them
|
|||||||
*/
|
*/
|
||||||
func BuildOverlay(nodeInfo node.NodeInfo, overlayNames []string) error {
|
func BuildOverlay(nodeInfo node.NodeInfo, overlayNames []string) error {
|
||||||
// create the dir where the overlay images will reside
|
// create the dir where the overlay images will reside
|
||||||
|
name := fmt.Sprintf("overlay %s/%v", nodeInfo.Id.Get(), overlayNames)
|
||||||
overlayImage := OverlayImage(nodeInfo.Id.Get(), overlayNames)
|
overlayImage := OverlayImage(nodeInfo.Id.Get(), overlayNames)
|
||||||
overlayImageDir := path.Dir(overlayImage)
|
overlayImageDir := path.Dir(overlayImage)
|
||||||
|
|
||||||
err := os.MkdirAll(overlayImageDir, 0755)
|
err := os.MkdirAll(overlayImageDir, 0755)
|
||||||
if err == nil {
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Created parent directory for Overlay Images: %s\n", overlayImageDir)
|
|
||||||
} else {
|
|
||||||
return errors.Wrap(err, "could not create overlay image directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
outputDir, err := ioutil.TempDir(os.TempDir(), ".wwctl-overlay-")
|
|
||||||
if err == nil {
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Creating temporary directory for overlay files: %s\n", outputDir)
|
|
||||||
} else {
|
|
||||||
return errors.Wrap(err, "could not create overlay temporary directory")
|
|
||||||
}
|
|
||||||
err = BuildOverlayIndir(nodeInfo, overlayNames, outputDir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.WARN, "Got following error when building overlay: %s\n", err)
|
return errors.Wrapf(err, "Failed to create directory for %s: %s", name, overlayImageDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Finished generating overlay working directory for: %s/%v\n", nodeInfo.Id.Get(), overlayNames)
|
wwlog.Debug("Created directory for %s: %s", name, overlayImageDir)
|
||||||
compressor, err := exec.LookPath("pigz")
|
|
||||||
|
buildDir, err := ioutil.TempDir(os.TempDir(), ".wwctl-overlay-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Could not locate PIGZ, using GZIP\n")
|
return errors.Wrapf(err, "Failed to create temporary directory for %s", name )
|
||||||
compressor = "gzip"
|
|
||||||
} else {
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Using PIGZ to compress the overlay: %s\n", compressor)
|
|
||||||
}
|
}
|
||||||
|
defer os.RemoveAll(buildDir)
|
||||||
|
|
||||||
cmd := fmt.Sprintf("cd \"%s\"; find . | cpio --quiet -o -H newc | %s -c > \"%s\"", outputDir, compressor, overlayImage)
|
wwlog.Debug("Created temporary directory for %s: %s", name, buildDir)
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd)
|
err = BuildOverlayIndir(nodeInfo, overlayNames, buildDir)
|
||||||
err = exec.Command("/bin/sh", "-c", cmd).Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not generate compressed runtime image overlay")
|
return errors.Wrapf(err, "Failed to generate files for %s", name)
|
||||||
}
|
}
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Completed building overlay image: %s\n", overlayImage)
|
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Removing temporary directory: %s\n", outputDir)
|
wwlog.Debug("Generated files for %s", name)
|
||||||
os.RemoveAll(outputDir)
|
|
||||||
return nil
|
err = util.BuildFsImage(
|
||||||
|
name,
|
||||||
|
buildDir,
|
||||||
|
overlayImage,
|
||||||
|
[]string{"*"},
|
||||||
|
[]string{},
|
||||||
|
// ignore cross-device files
|
||||||
|
true,
|
||||||
|
"newc")
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -199,38 +194,36 @@ exists it will be created.
|
|||||||
*/
|
*/
|
||||||
func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir string) error {
|
func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir string) error {
|
||||||
if len(overlayNames) == 0 {
|
if len(overlayNames) == 0 {
|
||||||
return errors.New("At least one valid overlay is needed to build for a node\n")
|
return errors.New("At least one valid overlay is needed to build for a node")
|
||||||
}
|
}
|
||||||
if !util.IsDir(outputDir) {
|
if !util.IsDir(outputDir) {
|
||||||
return errors.New(fmt.Sprintf("output %s must a be a directory\n", outputDir))
|
return errors.Errorf("output must a be a directory: %s", outputDir)
|
||||||
}
|
}
|
||||||
controller, err := warewulfconf.New()
|
controller, err := warewulfconf.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
nodeDB, err := node.New()
|
nodeDB, err := node.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
allNodes, err := nodeDB.FindAllNodes()
|
allNodes, err := nodeDB.FindAllNodes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.ValidString(strings.Join(overlayNames, ""), "^[a-zA-Z0-9-._:]+$") {
|
if !util.ValidString(strings.Join(overlayNames, ""), "^[a-zA-Z0-9-._:]+$") {
|
||||||
return errors.New(fmt.Sprintf("overlay names contains illegal characters: %v", overlayNames))
|
return errors.Errorf("overlay names contains illegal characters: %v", overlayNames)
|
||||||
}
|
}
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Processing node/overlay: %s/%s\n", nodeInfo.Id.Get(), strings.Join(overlayNames, "-"))
|
wwlog.Verbose("Processing node/overlay: %s/%s", nodeInfo.Id.Get(), strings.Join(overlayNames, "-"))
|
||||||
var tstruct TemplateStruct
|
var tstruct TemplateStruct
|
||||||
tstruct.Kernel = new(node.KernelConf)
|
tstruct.Kernel = new(node.KernelConf)
|
||||||
tstruct.Ipmi = new(node.IpmiConf)
|
tstruct.Ipmi = new(node.IpmiConf)
|
||||||
tstruct.Id = nodeInfo.Id.Get()
|
tstruct.Id = nodeInfo.Id.Get()
|
||||||
tstruct.Hostname = nodeInfo.Id.Get()
|
tstruct.Hostname = nodeInfo.Id.Get()
|
||||||
tstruct.Id = nodeInfo.Id.Get()
|
|
||||||
tstruct.Hostname = nodeInfo.Id.Get()
|
|
||||||
tstruct.ClusterName = nodeInfo.ClusterName.Get()
|
tstruct.ClusterName = nodeInfo.ClusterName.Get()
|
||||||
tstruct.Container = nodeInfo.ContainerName.Get()
|
tstruct.Container = nodeInfo.ContainerName.Get()
|
||||||
tstruct.Kernel.Version = nodeInfo.Kernel.Override.Get()
|
tstruct.Kernel.Version = nodeInfo.Kernel.Override.Get()
|
||||||
@@ -302,28 +295,28 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST")
|
tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST")
|
||||||
tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
|
tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
|
||||||
for _, overlayName := range overlayNames {
|
for _, overlayName := range overlayNames {
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Building overlay %s for node %s in %s\n", overlayName, nodeInfo.Id.Get(), outputDir)
|
wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeInfo.Id.Get(), outputDir)
|
||||||
overlaySourceDir := OverlaySourceDir(overlayName)
|
overlaySourceDir := OverlaySourceDir(overlayName)
|
||||||
wwlog.Printf(wwlog.DEBUG, "Starting to build overlay %s\nChanging directory to OverlayDir: %s\n", overlayName, overlaySourceDir)
|
wwlog.Debug("Starting to build overlay %s\nChanging directory to OverlayDir: %s", overlayName, overlaySourceDir)
|
||||||
err = os.Chdir(overlaySourceDir)
|
err = os.Chdir(overlaySourceDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not change directory to overlay dir")
|
return errors.Wrap(err, "could not change directory to overlay dir")
|
||||||
}
|
}
|
||||||
wwlog.Printf(wwlog.DEBUG, "Checking to see if overlay directory exists: %s\n", overlaySourceDir)
|
wwlog.Debug("Checking to see if overlay directory exists: %s", overlaySourceDir)
|
||||||
if !util.IsDir(overlaySourceDir) {
|
if !util.IsDir(overlaySourceDir) {
|
||||||
return errors.New("overlay does not exist: " + overlayName)
|
return errors.New("overlay does not exist: " + overlayName)
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Walking the overlay structure: %s\n", overlaySourceDir)
|
wwlog.Verbose("Walking the overlay structure: %s", overlaySourceDir)
|
||||||
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
|
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error for "+location)
|
return errors.Wrap(err, "error for "+location)
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Found overlay file: %s\n", location)
|
wwlog.Debug("Found overlay file: %s", location)
|
||||||
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Found directory: %s\n", location)
|
wwlog.Debug("Found directory: %s", location)
|
||||||
|
|
||||||
err = os.MkdirAll(path.Join(outputDir, location), info.Mode())
|
err = os.MkdirAll(path.Join(outputDir, location), info.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -334,11 +327,11 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
return errors.Wrap(err, "failed setting permissions on overlay directory")
|
return errors.Wrap(err, "failed setting permissions on overlay directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Created directory in overlay: %s\n", location)
|
wwlog.Debug("Created directory in overlay: %s", location)
|
||||||
|
|
||||||
} else if filepath.Ext(location) == ".ww" {
|
} else if filepath.Ext(location) == ".ww" {
|
||||||
tstruct.BuildSource = path.Join(overlaySourceDir, location)
|
tstruct.BuildSource = path.Join(overlaySourceDir, location)
|
||||||
wwlog.Printf(wwlog.VERBOSE, "Evaluating overlay template file: %s\n", location)
|
wwlog.Verbose("Evaluating overlay template file: %s", location)
|
||||||
destFile := strings.TrimSuffix(location, ".ww")
|
destFile := strings.TrimSuffix(location, ".ww")
|
||||||
backupFile := true
|
backupFile := true
|
||||||
writeFile := true
|
writeFile := true
|
||||||
@@ -351,12 +344,12 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
"dec": func(i int) int { return i - 1 },
|
"dec": func(i int) int { return i - 1 },
|
||||||
"file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
|
"file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
|
||||||
"abort": func() string {
|
"abort": func() string {
|
||||||
wwlog.Printf(wwlog.DEBUG, "abort file called in %s\n", location)
|
wwlog.Debug("abort file called in %s", location)
|
||||||
writeFile = false
|
writeFile = false
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
"nobackup": func() string {
|
"nobackup": func() string {
|
||||||
wwlog.Printf(wwlog.DEBUG, "not backup for %s\n", location)
|
wwlog.Debug("not backup for %s", location)
|
||||||
backupFile = false
|
backupFile = false
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
@@ -372,7 +365,6 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
err = tmpl.Execute(&buffer, tstruct)
|
err = tmpl.Execute(&buffer, tstruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not execute template")
|
return errors.Wrap(err, "could not execute template")
|
||||||
|
|
||||||
}
|
}
|
||||||
if writeFile {
|
if writeFile {
|
||||||
destFileName := destFile
|
destFileName := destFile
|
||||||
@@ -386,7 +378,7 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
line := fileScanner.Text()
|
line := fileScanner.Text()
|
||||||
filenameFromTemplate := reg.FindAllStringSubmatch(line, -1)
|
filenameFromTemplate := reg.FindAllStringSubmatch(line, -1)
|
||||||
if len(filenameFromTemplate) != 0 {
|
if len(filenameFromTemplate) != 0 {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Found multifile comment, new filename %s\n", filenameFromTemplate[0][1])
|
wwlog.Debug("Found multifile comment, new filename %s", filenameFromTemplate[0][1])
|
||||||
if foundFileComment {
|
if foundFileComment {
|
||||||
err = carefulWriteBuffer(path.Join(outputDir, destFileName),
|
err = carefulWriteBuffer(path.Join(outputDir, destFileName),
|
||||||
fileBuffer, backupFile, info.Mode())
|
fileBuffer, backupFile, info.Mode())
|
||||||
@@ -414,26 +406,26 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
return errors.Wrap(err, "failed setting permissions on template output file")
|
return errors.Wrap(err, "failed setting permissions on template output file")
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Wrote template file into overlay: %s\n", destFile)
|
wwlog.Debug("Wrote template file into overlay: %s", destFile)
|
||||||
|
|
||||||
// } else if b, _ := regexp.MatchString(`\.ww[a-zA-Z0-9\-\._]*$`, location); b {
|
// } else if b, _ := regexp.MatchString(`\.ww[a-zA-Z0-9\-\._]*$`, location); b {
|
||||||
// wwlog.Printf(wwlog.DEBUG, "Ignoring WW template file: %s\n", location)
|
// wwlog.Debug("Ignoring WW template file: %s", location)
|
||||||
}
|
}
|
||||||
} else if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
} else if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Found symlink %s\n", location)
|
wwlog.Debug("Found symlink %s", location)
|
||||||
destination, err := os.Readlink(location)
|
destination, err := os.Readlink(location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
}
|
}
|
||||||
err = os.Symlink(destination, path.Join(outputDir, location))
|
err = os.Symlink(destination, path.Join(outputDir, location))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
wwlog.ErrorExc(err, "")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
err := util.CopyFile(location, path.Join(outputDir, location))
|
err := util.CopyFile(location, path.Join(outputDir, location))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Copied file into overlay: %s\n", location)
|
wwlog.Debug("Copied file into overlay: %s", location)
|
||||||
} else {
|
} else {
|
||||||
return errors.Wrap(err, "could not copy file into overlay")
|
return errors.Wrap(err, "could not copy file into overlay")
|
||||||
}
|
}
|
||||||
@@ -453,13 +445,12 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
Writes buffer to the destination file. If wwbackup is set a wwbackup will be created.
|
Writes buffer to the destination file. If wwbackup is set a wwbackup will be created.
|
||||||
*/
|
*/
|
||||||
func carefulWriteBuffer(destFile string, buffer bytes.Buffer, backupFile bool, perm fs.FileMode) error {
|
func carefulWriteBuffer(destFile string, buffer bytes.Buffer, backupFile bool, perm fs.FileMode) error {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Trying to careful write file %s\n", destFile)
|
wwlog.Debug("Trying to careful write file (%d bytes): %s", buffer.Len(), destFile )
|
||||||
if backupFile {
|
if backupFile {
|
||||||
// if !util.IsFile(path.Join(outputDir, destFile+".wwbackup")) && util.IsFile(path.Join(outputDir, destFile)) {
|
|
||||||
if !util.IsFile(destFile+".wwbackup") && util.IsFile(destFile) {
|
if !util.IsFile(destFile+".wwbackup") && util.IsFile(destFile) {
|
||||||
err := util.CopyFile(destFile, destFile+".wwbackup")
|
err := util.CopyFile(destFile, destFile+".wwbackup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
return errors.Wrapf(err, "Failed to create backup: %s -> %s.wwbackup", destFile, destFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -18,6 +20,18 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func FirstError(errs ...error) (err error) {
|
||||||
|
for _, e := range errs {
|
||||||
|
if err == nil {
|
||||||
|
err = e
|
||||||
|
}else if e != nil {
|
||||||
|
wwlog.ErrorExc(e, "Unhandled error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func DirModTime(path string) (time.Time, error) {
|
func DirModTime(path string) (time.Time, error) {
|
||||||
|
|
||||||
var lastTime time.Time
|
var lastTime time.Time
|
||||||
@@ -45,17 +59,17 @@ func DirModTime(path string) (time.Time, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PathIsNewer(source string, compare string) bool {
|
func PathIsNewer(source string, compare string) bool {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Comparing times on paths: '%s' - '%s'\n", source, compare)
|
wwlog.Debug("Comparing times on paths: '%s' - '%s'", source, compare)
|
||||||
|
|
||||||
time1, err := DirModTime(source)
|
time1, err := DirModTime(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.DEBUG, "%s\n", err)
|
wwlog.DebugExc(err, "")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
time2, err := DirModTime(compare)
|
time2, err := DirModTime(compare)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.DEBUG, "%s\n", err)
|
wwlog.DebugExc(err, "")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +114,7 @@ func SliceInSlice(A []string, B []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsDir(path string) bool {
|
func IsDir(path string) bool {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Checking if path exists as a directory: %s\n", path)
|
wwlog.Debug("Checking if path exists as a directory: %s", path)
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return false
|
return false
|
||||||
@@ -112,7 +126,7 @@ func IsDir(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsFile(path string) bool {
|
func IsFile(path string) bool {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Checking if path exists as a file: %s\n", path)
|
wwlog.Debug("Checking if path exists as a file: %s", path)
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return false
|
return false
|
||||||
@@ -147,18 +161,19 @@ func ValidString(pattern string, expr string) bool {
|
|||||||
|
|
||||||
func ValidateOrDie(message string, pattern string, expr string) {
|
func ValidateOrDie(message string, pattern string, expr string) {
|
||||||
if ValidString(pattern, expr) {
|
if ValidString(pattern, expr) {
|
||||||
wwlog.Printf(wwlog.ERROR, "%s does not validate: '%s'\n", message, pattern)
|
wwlog.Error("%s does not validate: '%s'", message, pattern)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
func FindFiles(path string) []string {
|
func FindFiles(path string) []string {
|
||||||
var ret []string
|
var ret []string
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Changing directory to FindFiles path: %s\n", path)
|
wwlog.Debug("Changing directory to FindFiles path: %s", path)
|
||||||
err := os.Chdir(path)
|
err := os.Chdir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.WARN, "Could not chdir() to: %s\n", path)
|
wwlog.Warn("Could not chdir() to: %s", path)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,10 +187,10 @@ func FindFiles(path string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if IsDir(location) {
|
if IsDir(location) {
|
||||||
wwlog.Printf(wwlog.DEBUG, "FindFiles() found directory: %s\n", location)
|
wwlog.Debug("FindFiles() found directory: %s", location)
|
||||||
ret = append(ret, location+"/")
|
ret = append(ret, location+"/")
|
||||||
} else {
|
} else {
|
||||||
wwlog.Printf(wwlog.DEBUG, "FindFiles() found file: %s\n", location)
|
wwlog.Debug("FindFiles() found file: %s", location)
|
||||||
ret = append(ret, location)
|
ret = append(ret, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +203,128 @@ func FindFiles(path string) []string {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
func FindFilterFiles(
|
||||||
|
path string,
|
||||||
|
include []string,
|
||||||
|
ignore []string,
|
||||||
|
ignore_xdev bool) (ofiles []string, err error) {
|
||||||
|
|
||||||
|
wwlog.Debug("Finding files: %s", path)
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return ofiles, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = FirstError(err, os.Chdir(cwd))
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = os.Chdir(path)
|
||||||
|
if err != nil {
|
||||||
|
return ofiles, errors.Wrapf(err, "Failed to change path: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
files := []string{}
|
||||||
|
|
||||||
|
for _, pattern := range include {
|
||||||
|
|
||||||
|
_files, err := filepath.Glob(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return ofiles, errors.Wrapf(err, "Failed to apply pattern: %s", pattern)
|
||||||
|
}
|
||||||
|
wwlog.Debug("Including pattern: %s -> %d matches", pattern, len(_files))
|
||||||
|
|
||||||
|
files = append(files, _files...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for i, pattern := range(ignore) {
|
||||||
|
if strings.HasPrefix(pattern, "./") {
|
||||||
|
ignore[i] = pattern[2:]
|
||||||
|
}
|
||||||
|
wwlog.Debug("Ignore pattern (%d): %s", i, ignore[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ignore_xdev {
|
||||||
|
wwlog.Debug("Ignoring cross-device (xdev) files")
|
||||||
|
}
|
||||||
|
|
||||||
|
path_stat, err := os.Stat(".")
|
||||||
|
if err != nil {
|
||||||
|
return ofiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dev := path_stat.Sys().(*syscall.Stat_t).Dev
|
||||||
|
|
||||||
|
for _, ifile := range files {
|
||||||
|
stat, err := os.Stat(ifile)
|
||||||
|
if err != nil {
|
||||||
|
return ofiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.IsDir() {
|
||||||
|
// recursivly include from the matched directory
|
||||||
|
|
||||||
|
num_init := len(ofiles)
|
||||||
|
err = filepath.Walk(ifile, func(location string, info os.FileInfo, err error) error {
|
||||||
|
var file string
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if location == "." {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
file = location + "/"
|
||||||
|
} else {
|
||||||
|
file = location
|
||||||
|
}
|
||||||
|
|
||||||
|
if ignore_xdev && info.Sys().(*syscall.Stat_t).Dev != dev {
|
||||||
|
wwlog.Debug("Ignored (cross-device): %s", file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, pattern := range(ignore) {
|
||||||
|
m, err := filepath.Match(pattern, location)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m {
|
||||||
|
wwlog.Debug("Ignored (%d): %s", i, file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ofiles = append(ofiles, file)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
num_final := len(ofiles)
|
||||||
|
wwlog.Debug("Included: %s -> %d files", ifile, num_final-num_init)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ofiles, err
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
wwlog.Debug("Included: %s", ifile)
|
||||||
|
ofiles = append(ofiles, ifile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ofiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
func ExecInteractive(command string, a ...string) error {
|
func ExecInteractive(command string, a ...string) error {
|
||||||
wwlog.Printf(wwlog.DEBUG, "ExecInteractive(%s, %s)\n", command, a)
|
wwlog.Debug("ExecInteractive(%s, %s)", command, a)
|
||||||
c := exec.Command(command, a...)
|
c := exec.Command(command, a...)
|
||||||
c.Stdin = os.Stdin
|
c.Stdin = os.Stdin
|
||||||
c.Stdout = os.Stdout
|
c.Stdout = os.Stdout
|
||||||
@@ -223,7 +358,7 @@ func SliceRemoveElement(array []string, remove string) []string {
|
|||||||
if r != remove {
|
if r != remove {
|
||||||
ret = append(ret, r)
|
ret = append(ret, r)
|
||||||
} else {
|
} else {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Removing slice from array: %s\n", remove)
|
wwlog.Debug("Removing slice from array: %s", remove)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +402,7 @@ func SystemdStart(systemdName string) error {
|
|||||||
startCmd := fmt.Sprintf("systemctl restart %s", systemdName)
|
startCmd := fmt.Sprintf("systemctl restart %s", systemdName)
|
||||||
enableCmd := fmt.Sprintf("systemctl enable %s", systemdName)
|
enableCmd := fmt.Sprintf("systemctl enable %s", systemdName)
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Setting up Systemd service: %s\n", systemdName)
|
wwlog.Debug("Setting up Systemd service: %s", systemdName)
|
||||||
err := ExecInteractive("/bin/sh", "-c", startCmd)
|
err := ExecInteractive("/bin/sh", "-c", startCmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to run start cmd")
|
return errors.Wrap(err, "failed to run start cmd")
|
||||||
@@ -293,7 +428,7 @@ func CopyUIDGID(source string, dest string) error {
|
|||||||
UID = int(stat.Uid)
|
UID = int(stat.Uid)
|
||||||
GID = int(stat.Gid)
|
GID = int(stat.Gid)
|
||||||
}
|
}
|
||||||
wwlog.Printf(wwlog.DEBUG, "Chown %d:%d '%s'\n", UID, GID, dest)
|
wwlog.Debug("Chown %d:%d '%s'", UID, GID, dest)
|
||||||
err = os.Chown(dest, UID, GID)
|
err = os.Chown(dest, UID, GID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -357,18 +492,165 @@ func IncrementIPv4(start string, inc uint) string {
|
|||||||
Appending the lines to the given file
|
Appending the lines to the given file
|
||||||
*/
|
*/
|
||||||
func AppendLines(fileName string, lines []string) error {
|
func AppendLines(fileName string, lines []string) error {
|
||||||
wwlog.Printf(wwlog.VERBOSE, "appending %v lines to %s\n", len(lines), fileName)
|
wwlog.Verbose("appending %v lines to %s", len(lines), fileName)
|
||||||
file, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0644)
|
file, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("Can't open file %s: %s", fileName, err)
|
return errors.Wrapf(err, "Can't open file: %s", fileName)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
wwlog.Printf(wwlog.DEBUG, "Appending '%s' to %s\n", line, fileName)
|
wwlog.Debug("Appending '%s' to %s", line, fileName)
|
||||||
if _, err := file.WriteString(fmt.Sprintf("%s\n", line)); err != nil {
|
if _, err := file.WriteString(fmt.Sprintf("%s\n", line)); err != nil {
|
||||||
return errors.Errorf("Can't write to file %s: %s", fileName, err)
|
return errors.Wrapf(err, "Can't write to file: %s", fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
Create an archive using cpio
|
||||||
|
*/
|
||||||
|
func CpioCreate(
|
||||||
|
ifiles []string,
|
||||||
|
ofile string,
|
||||||
|
format string,
|
||||||
|
cpio_args ...string ) (err error) {
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"--quiet",
|
||||||
|
"--create",
|
||||||
|
"-H", format,
|
||||||
|
"--file=" + ofile }
|
||||||
|
|
||||||
|
args = append(args, cpio_args...)
|
||||||
|
|
||||||
|
proc := exec.Command("cpio", args...)
|
||||||
|
|
||||||
|
stdin, err := proc.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err_in := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
defer stdin.Close()
|
||||||
|
_, err := io.WriteString(stdin, strings.Join(ifiles, "\n"))
|
||||||
|
err_in <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
out, err := proc.CombinedOutput()
|
||||||
|
if len(out) > 0 {
|
||||||
|
wwlog.Debug(string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
return FirstError(err, <- err_in)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
Compress a file using gzip or pigz
|
||||||
|
*/
|
||||||
|
func FileGz(
|
||||||
|
file string ) (err error) {
|
||||||
|
|
||||||
|
file_gz := file + ".gz"
|
||||||
|
|
||||||
|
if IsFile(file_gz) {
|
||||||
|
err := os.Remove(file_gz)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Could not remove existing file: %s", file_gz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compressor, err := exec.LookPath("pigz")
|
||||||
|
if err != nil {
|
||||||
|
wwlog.Verbose("Could not locate PIGZ")
|
||||||
|
compressor = "gzip"
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Verbose("Using gz compressor: %s", compressor)
|
||||||
|
|
||||||
|
proc := exec.Command(
|
||||||
|
compressor,
|
||||||
|
"--keep",
|
||||||
|
file )
|
||||||
|
|
||||||
|
out, err := proc.CombinedOutput()
|
||||||
|
if len(out) > 0 {
|
||||||
|
wwlog.Debug(string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
Create an archive using cpio
|
||||||
|
*/
|
||||||
|
func BuildFsImage(
|
||||||
|
name string,
|
||||||
|
rootfsPath string,
|
||||||
|
imagePath string,
|
||||||
|
include []string,
|
||||||
|
ignore []string,
|
||||||
|
ignore_xdev bool,
|
||||||
|
format string,
|
||||||
|
cpio_args ...string ) (err error) {
|
||||||
|
|
||||||
|
err = os.MkdirAll(path.Dir(imagePath), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to create image directory for %s: %s", name, imagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Debug("Created image directory for %s: %s", name, imagePath)
|
||||||
|
|
||||||
|
// TODO: why is this done if the container must already exist?
|
||||||
|
err = os.MkdirAll(path.Dir(rootfsPath), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to create fs directory for %s: %s", name, rootfsPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Debug("Created fs directory for %s: %s", name, rootfsPath)
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = FirstError(err, os.Chdir(cwd))
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = os.Chdir(rootfsPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed chdir to fs directory for %s: %s", name, rootfsPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := FindFilterFiles(
|
||||||
|
".",
|
||||||
|
include,
|
||||||
|
ignore,
|
||||||
|
ignore_xdev )
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed discovering files for %s: %s", name, rootfsPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CpioCreate(
|
||||||
|
files,
|
||||||
|
imagePath,
|
||||||
|
format,
|
||||||
|
cpio_args...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed creating image for %s: %s", name, imagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Info("Created image for %s: %s", name, imagePath)
|
||||||
|
|
||||||
|
err = FileGz(imagePath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to compress image for %s: %s", name, imagePath + ".gz")
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Info("Compressed image for %s: %s", name, imagePath + ".gz")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
package warewulfd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ContainerSend(w http.ResponseWriter, req *http.Request) {
|
|
||||||
rinfo, err := parseReq(req)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node, err := GetNode(rinfo.hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(403)
|
|
||||||
daemonLogf("ERROR(%s): %s\n", rinfo.hwaddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: Incorrect asset key for node: %s\n", node.Id.Get())
|
|
||||||
updateStatus(node.Id.Get(), "CONTAINER", "BAD_ASSET", rinfo.ipaddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.ContainerName.Defined() {
|
|
||||||
containerImage := container.ImageFile(node.ContainerName.Get())
|
|
||||||
|
|
||||||
updateStatus(node.Id.Get(), "CONTAINER", node.ContainerName.Get()+".img", strings.Split(req.RemoteAddr, ":")[0])
|
|
||||||
|
|
||||||
err = sendFile(w, containerImage, node.Id.Get())
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
w.WriteHeader(503)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(503)
|
|
||||||
daemonLogf("WARNING: No Container set for node %s\n", node.Id.Get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package warewulfd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||||
"github.com/hpcng/warewulf/internal/pkg/version"
|
"github.com/hpcng/warewulf/internal/pkg/version"
|
||||||
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,37 +22,51 @@ const (
|
|||||||
WAREWULFD_LOGFILE = "/var/log/warewulfd.log"
|
WAREWULFD_LOGFILE = "/var/log/warewulfd.log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logwriter *syslog.Writer
|
|
||||||
var loginit bool
|
var loginit bool
|
||||||
|
|
||||||
func daemonLogf(message string, a ...interface{}) {
|
func DaemonFormatter(logLevel int, rec *wwlog.LogRecord) string {
|
||||||
|
return "[" + rec.Time.Format(time.UnixDate) + "] " + wwlog.DefaultFormatter(logLevel, rec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DaemonInitLogging() error {
|
||||||
|
if loginit {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.SetLogFormatter(DaemonFormatter)
|
||||||
|
|
||||||
|
level_str, ok := os.LookupEnv("WAREWULFD_LOGLEVEL")
|
||||||
|
if ok {
|
||||||
|
level, err := strconv.Atoi(level_str)
|
||||||
|
if err == nil {
|
||||||
|
wwlog.SetLogLevel(level)
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
wwlog.SetLogLevel(wwlog.SERV)
|
||||||
|
}
|
||||||
|
|
||||||
conf, err := warewulfconf.New()
|
conf, err := warewulfconf.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("ERROR: Could not read Warewulf configuration file: %s\n", err)
|
return errors.Wrap(err, "Could not read Warewulf configuration file")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.Warewulf.Syslog {
|
if conf.Warewulf.Syslog {
|
||||||
if !loginit {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
logwriter, err = syslog.New(syslog.LOG_NOTICE, "warewulfd")
|
wwlog.Debug("Changingq log output to syslog")
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.SetOutput(logwriter)
|
|
||||||
loginit = true
|
|
||||||
|
|
||||||
log.SetFlags(0)
|
logwriter, err := syslog.New(syslog.LOG_NOTICE, "warewulfd")
|
||||||
log.SetPrefix("")
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Could not create syslog writer")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf(message, a...)
|
wwlog.SetLogFormatter(wwlog.DefaultFormatter)
|
||||||
|
wwlog.SetLogWriters(logwriter, logwriter)
|
||||||
|
|
||||||
} else {
|
|
||||||
prefix := fmt.Sprintf("[%s] ", time.Now().Format(time.UnixDate))
|
|
||||||
fmt.Printf(prefix+message, a...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loginit = true
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DaemonStart() error {
|
func DaemonStart() error {
|
||||||
@@ -69,6 +83,13 @@ func DaemonStart() error {
|
|||||||
|
|
||||||
os.Setenv("WAREWULFD_BACKGROUND", "1")
|
os.Setenv("WAREWULFD_BACKGROUND", "1")
|
||||||
|
|
||||||
|
logLevel := wwlog.GetLogLevel()
|
||||||
|
if logLevel == wwlog.INFO {
|
||||||
|
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.SERV))
|
||||||
|
}else{
|
||||||
|
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(logLevel))
|
||||||
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile(WAREWULFD_LOGFILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
f, err := os.OpenFile(WAREWULFD_LOGFILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -91,7 +112,7 @@ func DaemonStart() error {
|
|||||||
|
|
||||||
fmt.Fprintf(p, "%d", pid)
|
fmt.Fprintf(p, "%d", pid)
|
||||||
|
|
||||||
fmt.Printf("Started Warewulf (%s) server at PID: %d\n", version.GetVersion(), pid)
|
wwlog.Serv("Started Warewulf (%s) server at PID: %d", version.GetVersion(), pid)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +138,7 @@ func DaemonStatus() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to send process SIGCONT")
|
return errors.Wrap(err, "failed to send process SIGCONT")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Warewulf server is running at PID: %d\n", pid)
|
wwlog.Serv("Warewulf server is running at PID: %d", pid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,12 +166,19 @@ func DaemonReload() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logLevel := wwlog.GetLogLevel()
|
||||||
|
if logLevel == wwlog.INFO {
|
||||||
|
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.SERV))
|
||||||
|
}else{
|
||||||
|
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(logLevel))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DaemonStop() error {
|
func DaemonStop() error {
|
||||||
if !util.IsFile(WAREWULFD_PIDFILE) {
|
if !util.IsFile(WAREWULFD_PIDFILE) {
|
||||||
fmt.Printf("Warewulf daemon process not running\n")
|
wwlog.Warn("Warewulf daemon process not running")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +199,7 @@ func DaemonStop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to send process SIGTERM")
|
return errors.Wrap(err, "failed to send process SIGTERM")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Terminated Warewulf server at PID: %d\n", pid)
|
wwlog.Serv("Terminated Warewulf server at PID: %d", pid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
package warewulfd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/buildconfig"
|
|
||||||
nodepkg "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"
|
|
||||||
)
|
|
||||||
|
|
||||||
type iPxeTemplate struct {
|
|
||||||
Message string
|
|
||||||
WaitTime string
|
|
||||||
Hostname string
|
|
||||||
Fqdn string
|
|
||||||
Id string
|
|
||||||
Cluster string
|
|
||||||
ContainerName string
|
|
||||||
Hwaddr string
|
|
||||||
Ipaddr string
|
|
||||||
Port string
|
|
||||||
KernelArgs string
|
|
||||||
KernelOverride string
|
|
||||||
}
|
|
||||||
|
|
||||||
func IpxeSend(w http.ResponseWriter, req *http.Request) {
|
|
||||||
conf, err := warewulfconf.New()
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: Could not open Warewulf configuration: %s\n", err)
|
|
||||||
w.WriteHeader(503)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rinfo, err := parseReq(req)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
node, err := GetNode(rinfo.hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
// If we failed to find a node, let's see if we can add one...
|
|
||||||
var netdev string
|
|
||||||
var unconfiguredNode bool
|
|
||||||
|
|
||||||
daemonLogf("IPXEREQ: %s (node not configured)\n", rinfo.hwaddr)
|
|
||||||
|
|
||||||
nodeDB, err := nodepkg.New()
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("Could not read node configuration file: %s\n", err)
|
|
||||||
w.WriteHeader(503)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n, netdev, err := nodeDB.FindDiscoverableNode()
|
|
||||||
if err != nil {
|
|
||||||
unconfiguredNode = true
|
|
||||||
|
|
||||||
} else {
|
|
||||||
n.NetDevs[netdev].Hwaddr.Set(rinfo.hwaddr)
|
|
||||||
n.Discoverable.SetB(false)
|
|
||||||
err := nodeDB.NodeUpdate(n)
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("IPXEREQ: %s (failed to set node configuration)\n", rinfo.hwaddr)
|
|
||||||
|
|
||||||
unconfiguredNode = true
|
|
||||||
} else {
|
|
||||||
err := nodeDB.Persist()
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("IPXEREQ: %s (failed to persist node configuration)\n", rinfo.hwaddr)
|
|
||||||
|
|
||||||
unconfiguredNode = true
|
|
||||||
} else {
|
|
||||||
node = n
|
|
||||||
_ = overlay.BuildAllOverlays([]nodepkg.NodeInfo{n})
|
|
||||||
|
|
||||||
daemonLogf("IPXEREQ: %s (node automatically configured)\n", rinfo.hwaddr)
|
|
||||||
|
|
||||||
err := LoadNodeDB()
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("Could not reload configuration: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if unconfiguredNode {
|
|
||||||
daemonLogf("IPXEREQ: %s (unknown/unconfigured node)\n", rinfo.hwaddr)
|
|
||||||
|
|
||||||
tmpl, err := template.ParseFiles(path.Join(buildconfig.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe"))
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: Could not parse unconfigured node IPXE template: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var replace iPxeTemplate
|
|
||||||
|
|
||||||
replace.Hwaddr = rinfo.hwaddr
|
|
||||||
|
|
||||||
err = tmpl.Execute(w, replace)
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: Could not update unconfigured node IPXE template: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: Incorrect asset key for node: %s\n", node.Id.Get())
|
|
||||||
updateStatus(node.Id.Get(), "IPXE", "BAD_ASSET", rinfo.ipaddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ipxeTemplate := path.Join(buildconfig.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe")
|
|
||||||
|
|
||||||
tmpl, err := template.ParseFiles(ipxeTemplate)
|
|
||||||
if err != nil {
|
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var replace iPxeTemplate
|
|
||||||
|
|
||||||
replace.Id = node.Id.Get()
|
|
||||||
replace.Cluster = node.ClusterName.Get()
|
|
||||||
replace.Fqdn = node.Id.Get()
|
|
||||||
replace.Ipaddr = conf.Ipaddr
|
|
||||||
replace.Port = strconv.Itoa(conf.Warewulf.Port)
|
|
||||||
replace.Hostname = node.Id.Get()
|
|
||||||
replace.Hwaddr = rinfo.hwaddr
|
|
||||||
replace.ContainerName = node.ContainerName.Get()
|
|
||||||
replace.KernelArgs = node.Kernel.Args.Get()
|
|
||||||
replace.KernelOverride = node.Kernel.Override.Get()
|
|
||||||
|
|
||||||
err = tmpl.Execute(w, replace)
|
|
||||||
if err != nil {
|
|
||||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
daemonLogf("SEND: %15s: %s\n", node.Id.Get(), ipxeTemplate)
|
|
||||||
|
|
||||||
updateStatus(node.Id.Get(), "IPXE", node.Ipxe.Get()+".ipxe", strings.Split(req.RemoteAddr, ":")[0])
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package warewulfd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/kernel"
|
|
||||||
)
|
|
||||||
|
|
||||||
func KernelSend(w http.ResponseWriter, req *http.Request) {
|
|
||||||
rinfo, err := parseReq(req)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node, err := GetNode(rinfo.hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(403)
|
|
||||||
daemonLogf("ERROR(%s): %s\n", rinfo.hwaddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: Incorrect asset key for node: %s\n", node.Id.Get())
|
|
||||||
updateStatus(node.Id.Get(), "KERNEL", "BAD_ASSET", rinfo.ipaddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName string
|
|
||||||
if node.Kernel.Override.Defined() {
|
|
||||||
fileName = kernel.KernelImage(node.Kernel.Override.Get())
|
|
||||||
|
|
||||||
} else if node.ContainerName.Defined() {
|
|
||||||
fileName = container.KernelFind(node.ContainerName.Get())
|
|
||||||
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(503)
|
|
||||||
daemonLogf("WARNING: No 'kernel version' set for node %s\n", node.Id.Get())
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStatus(node.Id.Get(), "KERNEL", path.Base(fileName), strings.Split(req.RemoteAddr, ":")[0])
|
|
||||||
|
|
||||||
err = sendFile(w, fileName, node.Id.Get())
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package warewulfd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/kernel"
|
|
||||||
)
|
|
||||||
|
|
||||||
func KmodsSend(w http.ResponseWriter, req *http.Request) {
|
|
||||||
rinfo, err := parseReq(req)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node, err := GetNode(rinfo.hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(403)
|
|
||||||
daemonLogf("ERROR(%s): %s\n", rinfo.hwaddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: Incorrect asset key for node: %s\n", node.Id.Get())
|
|
||||||
updateStatus(node.Id.Get(), "KMODS_OVERLAY", "BAD_ASSET", rinfo.ipaddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Kernel.Override.Defined() {
|
|
||||||
fileName := kernel.KmodsImage(node.Kernel.Override.Get())
|
|
||||||
|
|
||||||
updateStatus(node.Id.Get(), "KMODS_OVERLAY", node.Kernel.Override.Get()+".img", strings.Split(req.RemoteAddr, ":")[0])
|
|
||||||
|
|
||||||
err := sendFile(w, fileName, node.Id.Get())
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,14 +14,24 @@ type parserInfo struct {
|
|||||||
remoteport int
|
remoteport int
|
||||||
assetkey string
|
assetkey string
|
||||||
uuid string
|
uuid string
|
||||||
|
stage string
|
||||||
overlay string
|
overlay string
|
||||||
|
compress string
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseReq(req *http.Request) (parserInfo, error) {
|
func parseReq(req *http.Request) (parserInfo, error) {
|
||||||
var ret parserInfo
|
var ret parserInfo
|
||||||
|
|
||||||
url := strings.Split(req.URL.Path, "?")[0]
|
url := strings.Split(req.URL.Path, "?")[0]
|
||||||
hwaddr := strings.Split(url, "/")[2]
|
path_parts := strings.Split(url, "/")
|
||||||
|
|
||||||
|
if len(path_parts) != 3 {
|
||||||
|
return ret, errors.New("unknown path components in GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle when stage was passed in the url path /[stage]/hwaddr
|
||||||
|
stage := path_parts[1]
|
||||||
|
hwaddr := path_parts[2]
|
||||||
hwaddr = strings.ReplaceAll(hwaddr, "-", ":")
|
hwaddr = strings.ReplaceAll(hwaddr, "-", ":")
|
||||||
hwaddr = strings.ToLower(hwaddr)
|
hwaddr = strings.ToLower(hwaddr)
|
||||||
|
|
||||||
@@ -36,9 +46,35 @@ func parseReq(req *http.Request) (parserInfo, error) {
|
|||||||
if len(req.URL.Query()["uuid"]) > 0 {
|
if len(req.URL.Query()["uuid"]) > 0 {
|
||||||
ret.uuid = req.URL.Query()["uuid"][0]
|
ret.uuid = req.URL.Query()["uuid"][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(req.URL.Query()["stage"]) > 0 {
|
||||||
|
ret.stage = req.URL.Query()["stage"][0]
|
||||||
|
}else{
|
||||||
|
|
||||||
|
if stage == "ipxe" || stage == "provision" {
|
||||||
|
ret.stage = "ipxe"
|
||||||
|
}else if stage == "kernel" {
|
||||||
|
ret.stage = "kernel"
|
||||||
|
}else if stage == "kmods" {
|
||||||
|
ret.stage = "kmods"
|
||||||
|
}else if stage == "container" {
|
||||||
|
ret.stage = "container"
|
||||||
|
}else if stage == "overlay-system" {
|
||||||
|
ret.stage = "system"
|
||||||
|
}else if stage == "overlay-runtime" {
|
||||||
|
ret.stage = "runtime"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(req.URL.Query()["overlay"]) > 0 {
|
if len(req.URL.Query()["overlay"]) > 0 {
|
||||||
ret.overlay = req.URL.Query()["overlay"][0]
|
ret.overlay = req.URL.Query()["overlay"][0]
|
||||||
}
|
}
|
||||||
|
if len(req.URL.Query()["compress"]) > 0 {
|
||||||
|
ret.compress = req.URL.Query()["compress"][0]
|
||||||
|
}
|
||||||
|
if ret.stage == "" {
|
||||||
|
return ret, errors.New("no stage encoded in GET")
|
||||||
|
}
|
||||||
if ret.hwaddr == "" {
|
if ret.hwaddr == "" {
|
||||||
return ret, errors.New("no hwaddr encoded in GET")
|
return ret, errors.New("no hwaddr encoded in GET")
|
||||||
}
|
}
|
||||||
|
|||||||
278
internal/pkg/warewulfd/provision.go
Normal file
278
internal/pkg/warewulfd/provision.go
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
package warewulfd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"bytes"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/buildconfig"
|
||||||
|
nodepkg "github.com/hpcng/warewulf/internal/pkg/node"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/kernel"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/overlay"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type iPxeTemplate struct {
|
||||||
|
Message string
|
||||||
|
WaitTime string
|
||||||
|
Hostname string
|
||||||
|
Fqdn string
|
||||||
|
Id string
|
||||||
|
Cluster string
|
||||||
|
ContainerName string
|
||||||
|
Hwaddr string
|
||||||
|
Ipaddr string
|
||||||
|
Port string
|
||||||
|
KernelArgs string
|
||||||
|
KernelOverride string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||||
|
conf, err := warewulfconf.New()
|
||||||
|
if err != nil {
|
||||||
|
wwlog.Error("Could not open Warewulf configuration: %s", err)
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rinfo, err := parseReq(req)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
wwlog.ErrorExc(err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Recv("hwaddr: %s, ipaddr: %s, stage: %s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage )
|
||||||
|
|
||||||
|
if rinfo.stage == "runtime" && conf.Warewulf.Secure {
|
||||||
|
if rinfo.remoteport >= 1024 {
|
||||||
|
wwlog.Denied("Non-privledged port: %s", req.RemoteAddr)
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_stages := map[string]string{
|
||||||
|
"ipxe": "IPXE",
|
||||||
|
"kernel": "KERNEL",
|
||||||
|
"kmods": "KMODS_OVERLAY",
|
||||||
|
"system": "SYSTEM_OVERLAY",
|
||||||
|
"runtime": "RUNTIME_OVERLAY" }
|
||||||
|
|
||||||
|
status_stage := status_stages[rinfo.stage]
|
||||||
|
var stage_overlays []string
|
||||||
|
var stage_file string = ""
|
||||||
|
// TODO: when module version is upgraded to go1.18, should be 'any' type
|
||||||
|
var tmpl_data interface{}
|
||||||
|
|
||||||
|
node, err := GetNode(rinfo.hwaddr)
|
||||||
|
if err != nil {
|
||||||
|
// If we failed to find a node, let's see if we can add one...
|
||||||
|
var netdev string
|
||||||
|
|
||||||
|
wwlog.Warn("%s (node not configured)", rinfo.hwaddr)
|
||||||
|
|
||||||
|
nodeDB, err := nodepkg.New()
|
||||||
|
if err != nil {
|
||||||
|
wwlog.Error("Could not read node configuration file: %s", err)
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n, netdev, err := nodeDB.FindDiscoverableNode()
|
||||||
|
if err == nil {
|
||||||
|
n.NetDevs[netdev].Hwaddr.Set(rinfo.hwaddr)
|
||||||
|
n.Discoverable.SetB(false)
|
||||||
|
err := nodeDB.NodeUpdate(n)
|
||||||
|
if err != nil {
|
||||||
|
wwlog.Serv("%s (failed to set node configuration)", rinfo.hwaddr)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err := nodeDB.Persist()
|
||||||
|
if err != nil {
|
||||||
|
wwlog.Serv("%s (failed to persist node configuration)", rinfo.hwaddr)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
node = n
|
||||||
|
_ = overlay.BuildAllOverlays([]nodepkg.NodeInfo{n})
|
||||||
|
|
||||||
|
wwlog.Serv("%s (node automatically configured)", rinfo.hwaddr)
|
||||||
|
|
||||||
|
err := LoadNodeDB()
|
||||||
|
if err != nil {
|
||||||
|
wwlog.Warn("Could not reload configuration: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
wwlog.Denied("Incorrect asset key for node: %s", node.Id.Get())
|
||||||
|
updateStatus(node.Id.Get(), status_stage, "BAD_ASSET", rinfo.ipaddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !node.Id.Defined() {
|
||||||
|
wwlog.Error("%s (unknown/unconfigured node)", rinfo.hwaddr)
|
||||||
|
if rinfo.stage == "ipxe" {
|
||||||
|
stage_file = path.Join(buildconfig.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe")
|
||||||
|
tmpl_data = iPxeTemplate{
|
||||||
|
Hwaddr : rinfo.hwaddr }
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if rinfo.stage == "ipxe" {
|
||||||
|
stage_file = path.Join(buildconfig.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe")
|
||||||
|
tmpl_data = iPxeTemplate{
|
||||||
|
Id : node.Id.Get(),
|
||||||
|
Cluster : node.ClusterName.Get(),
|
||||||
|
Fqdn : node.Id.Get(),
|
||||||
|
Ipaddr : conf.Ipaddr,
|
||||||
|
Port : strconv.Itoa(conf.Warewulf.Port),
|
||||||
|
Hostname : node.Id.Get(),
|
||||||
|
Hwaddr : rinfo.hwaddr,
|
||||||
|
ContainerName : node.ContainerName.Get(),
|
||||||
|
KernelArgs : node.Kernel.Args.Get(),
|
||||||
|
KernelOverride : node.Kernel.Override.Get() }
|
||||||
|
|
||||||
|
}else if rinfo.stage == "kernel" {
|
||||||
|
if node.Kernel.Override.Defined() {
|
||||||
|
stage_file = kernel.KernelImage(node.Kernel.Override.Get())
|
||||||
|
} else if node.ContainerName.Defined() {
|
||||||
|
stage_file = container.KernelFind(node.ContainerName.Get())
|
||||||
|
|
||||||
|
if stage_file == "" {
|
||||||
|
wwlog.Error("No kernel found for container %s", node.ContainerName.Get())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wwlog.Warn("No kernel version set for node %s", node.Id.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if rinfo.stage == "kmods" {
|
||||||
|
if node.Kernel.Override.Defined() {
|
||||||
|
stage_file = kernel.KmodsImage(node.Kernel.Override.Get())
|
||||||
|
}else{
|
||||||
|
wwlog.Warn("No kernel override modules set for node %s", node.Id.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if rinfo.stage == "container" {
|
||||||
|
if node.ContainerName.Defined() {
|
||||||
|
stage_file = container.ImageFile(node.ContainerName.Get())
|
||||||
|
} else {
|
||||||
|
wwlog.Warn("No container set for node %s", node.Id.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if rinfo.stage == "system" {
|
||||||
|
if len(node.SystemOverlay.GetSlice()) != 0 {
|
||||||
|
stage_overlays = node.SystemOverlay.GetSlice()
|
||||||
|
} else {
|
||||||
|
wwlog.Warn("No system overlay set for node %s", node.Id.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if rinfo.stage == "runtime" {
|
||||||
|
if rinfo.overlay != "" {
|
||||||
|
stage_overlays = []string{rinfo.overlay}
|
||||||
|
} else if len(node.RuntimeOverlay.GetSlice()) != 0 {
|
||||||
|
stage_overlays = node.RuntimeOverlay.GetSlice()
|
||||||
|
} else {
|
||||||
|
wwlog.Warn("No runtime overlay set for node %s", node.Id.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stage_overlays) > 0 {
|
||||||
|
stage_file = overlay.OverlayImage(node.Id.Get(), stage_overlays)
|
||||||
|
if conf.Warewulf.AutobuildOverlays {
|
||||||
|
oneoverlaynewer := false
|
||||||
|
for _, overlayname := range stage_overlays {
|
||||||
|
oneoverlaynewer = oneoverlaynewer || util.PathIsNewer(stage_file, overlay.OverlaySourceDir(overlayname))
|
||||||
|
}
|
||||||
|
if !util.IsFile(stage_file) || util.PathIsNewer(stage_file, nodepkg.ConfigFile) || oneoverlaynewer {
|
||||||
|
wwlog.Serv("BUILD %15s, overlays %v", node.Id.Get(), stage_overlays)
|
||||||
|
_ = overlay.BuildOverlay(node, stage_overlays)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Serv("stage_file '%s'", stage_file )
|
||||||
|
|
||||||
|
if util.IsFile(stage_file) {
|
||||||
|
|
||||||
|
if tmpl_data != nil {
|
||||||
|
if rinfo.compress != "" {
|
||||||
|
wwlog.Error("Unsupported %s compressed version for file: %s",
|
||||||
|
rinfo.compress, stage_file)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.ParseFiles(stage_file)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
wwlog.ErrorExc(err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// template engine writes file to buffer in case rendering fails
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
err = tmpl.Execute(&buf, tmpl_data)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
wwlog.ErrorExc(err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text")
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||||
|
_, err = buf.WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
wwlog.ErrorExc(err, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
wwlog.Send("%15s: %s", node.Id.Get(), stage_file)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
if rinfo.compress == "gz" {
|
||||||
|
stage_file += ".gz"
|
||||||
|
|
||||||
|
if !util.IsFile(stage_file) {
|
||||||
|
wwlog.Error("unprepared for compressed version of file %s",
|
||||||
|
stage_file)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}else if rinfo.compress != "" {
|
||||||
|
wwlog.Error("unsupported %s compressed version of file %s",
|
||||||
|
rinfo.compress, stage_file)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sendFile(w, stage_file, node.Id.Get())
|
||||||
|
if err != nil {
|
||||||
|
wwlog.ErrorExc(err, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(node.Id.Get(), status_stage, path.Base(stage_file), rinfo.ipaddr)
|
||||||
|
|
||||||
|
}else if stage_file == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
wwlog.Error("No resource selected")
|
||||||
|
updateStatus(node.Id.Get(), status_stage, "BAD_REQUEST", rinfo.ipaddr)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
wwlog.Error("Not found: %s", stage_file )
|
||||||
|
updateStatus(node.Id.Get(), status_stage, "NOT_FOUND", rinfo.ipaddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package warewulfd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
nodepkg "github.com/hpcng/warewulf/internal/pkg/node"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/overlay"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RuntimeOverlaySend(w http.ResponseWriter, req *http.Request) {
|
|
||||||
rinfo, err := parseReq(req)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node, err := GetNode(rinfo.hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(403)
|
|
||||||
daemonLogf("ERROR(%s): %s\n", rinfo.hwaddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: Incorrect asset key for node: %s\n", node.Id.Get())
|
|
||||||
updateStatus(node.Id.Get(), "RUNTIME_OVERLAY", "BAD_ASSET", rinfo.ipaddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conf, err := warewulfconf.New()
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: Could not read Warewulf configuration file: %s\n", err)
|
|
||||||
w.WriteHeader(503)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf.Warewulf.Secure {
|
|
||||||
if rinfo.remoteport >= 1024 {
|
|
||||||
daemonLogf("DENIED: Connection coming from non-privledged port: %s\n", req.RemoteAddr)
|
|
||||||
w.WriteHeader(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rinfo.overlay != "" {
|
|
||||||
fileName := overlay.OverlayImage(node.Id.Get(), []string{rinfo.overlay})
|
|
||||||
if conf.Warewulf.AutobuildOverlays {
|
|
||||||
_ = overlay.BuildOverlay(node, []string{rinfo.overlay})
|
|
||||||
}
|
|
||||||
err := sendFile(w, fileName, node.Id.Get())
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
}
|
|
||||||
} else if len(node.RuntimeOverlay.GetSlice()) != 0 {
|
|
||||||
fileName := overlay.OverlayImage(node.Id.Get(), node.RuntimeOverlay.GetSlice())
|
|
||||||
|
|
||||||
updateStatus(node.Id.Get(), "RUNTIME_OVERLAY", node.RuntimeOverlay.Get(), strings.Split(req.RemoteAddr, ":")[0])
|
|
||||||
|
|
||||||
if conf.Warewulf.AutobuildOverlays {
|
|
||||||
oneoverlaynewer := false
|
|
||||||
for _, overlayname := range node.RuntimeOverlay.GetSlice() {
|
|
||||||
oneoverlaynewer = oneoverlaynewer || util.PathIsNewer(fileName, overlay.OverlaySourceDir(overlayname))
|
|
||||||
}
|
|
||||||
if !util.IsFile(fileName) || util.PathIsNewer(fileName, nodepkg.ConfigFile) || oneoverlaynewer {
|
|
||||||
daemonLogf("BUILD: %15s: Runtime Overlay\n", node.Id.Get())
|
|
||||||
_ = overlay.BuildOverlay(node, node.RuntimeOverlay.GetSlice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := sendFile(w, fileName, node.Id.Get())
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(503)
|
|
||||||
daemonLogf("WARNING: No 'runtime overlay' set for node %s\n", node.Id.Get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -58,7 +58,7 @@ func LoadNodeStatus() error {
|
|||||||
func updateStatus(nodeID, stage, sent, ipaddr string) {
|
func updateStatus(nodeID, stage, sent, ipaddr string) {
|
||||||
rightnow := time.Now().Unix()
|
rightnow := time.Now().Unix()
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Updating node status data: %s\n", nodeID)
|
wwlog.Debug("Updating node status data: %s", nodeID)
|
||||||
|
|
||||||
var n NodeStatus
|
var n NodeStatus
|
||||||
n.NodeName = nodeID
|
n.NodeName = nodeID
|
||||||
@@ -71,7 +71,7 @@ func updateStatus(nodeID, stage, sent, ipaddr string) {
|
|||||||
|
|
||||||
func statusJSON() ([]byte, error) {
|
func statusJSON() ([]byte, error) {
|
||||||
|
|
||||||
wwlog.Printf(wwlog.DEBUG, "Request for node status data...\n")
|
wwlog.Debug("Request for node status data...")
|
||||||
|
|
||||||
ret, err := json.MarshalIndent(statusDB, "", " ")
|
ret, err := json.MarshalIndent(statusDB, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -85,11 +85,12 @@ func StatusSend(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
status, err := statusJSON()
|
status, err := statusJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = w.Write(status)
|
_, err = w.Write(status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.WARN, "Could not send status JSON: %s\n", err)
|
wwlog.Warn("Could not send status JSON: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package warewulfd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
nodepkg "github.com/hpcng/warewulf/internal/pkg/node"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/overlay"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SystemOverlaySend(w http.ResponseWriter, req *http.Request) {
|
|
||||||
rinfo, err := parseReq(req)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node, err := GetNode(rinfo.hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(403)
|
|
||||||
daemonLogf("ERROR(%s): %s\n", rinfo.hwaddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
daemonLogf("ERROR: Incorrect asset key for node: %s\n", node.Id.Get())
|
|
||||||
updateStatus(node.Id.Get(), "SYSTEM_OVERLAY", "BAD_ASSET", rinfo.ipaddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conf, err := warewulfconf.New()
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: Could not read Warewulf configuration file: %s\n", err)
|
|
||||||
w.WriteHeader(503)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(node.SystemOverlay.GetSlice()) != 0 {
|
|
||||||
fileName := overlay.OverlayImage(node.Id.Get(), node.SystemOverlay.GetSlice())
|
|
||||||
|
|
||||||
updateStatus(node.Id.Get(), "SYSTEM_OVERLAY", node.SystemOverlay.Get(), strings.Split(req.RemoteAddr, ":")[0])
|
|
||||||
|
|
||||||
if conf.Warewulf.AutobuildOverlays {
|
|
||||||
oneoverlaynewer := false
|
|
||||||
for _, overlayname := range node.RuntimeOverlay.GetSlice() {
|
|
||||||
oneoverlaynewer = oneoverlaynewer || util.PathIsNewer(fileName, overlay.OverlaySourceDir(overlayname))
|
|
||||||
}
|
|
||||||
if !util.IsFile(fileName) || util.PathIsNewer(fileName, nodepkg.ConfigFile) || oneoverlaynewer {
|
|
||||||
daemonLogf("BUILD: %15s: System Overlay\n", node.Id.Get())
|
|
||||||
_ = overlay.BuildOverlay(node, node.SystemOverlay.GetSlice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := sendFile(w, fileName, node.Id.Get())
|
|
||||||
if err != nil {
|
|
||||||
daemonLogf("ERROR: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(503)
|
|
||||||
daemonLogf("WARNING: No 'system system-overlay' set for node %s\n", node.Id.Get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,16 +2,19 @@ package warewulfd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sendFile(w http.ResponseWriter, filename string, sendto string) error {
|
func sendFile(w http.ResponseWriter, filename string, sendto string) error {
|
||||||
fd, err := os.Open(filename)
|
fd, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
@@ -19,27 +22,33 @@ func sendFile(w http.ResponseWriter, filename string, sendto string) error {
|
|||||||
FileHeader := make([]byte, 512)
|
FileHeader := make([]byte, 512)
|
||||||
_, err = fd.Read(FileHeader)
|
_, err = fd.Read(FileHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return errors.Wrap(err, "failed to read header")
|
return errors.Wrap(err, "failed to read header")
|
||||||
}
|
}
|
||||||
|
|
||||||
FileContentType := http.DetectContentType(FileHeader)
|
FileContentType := http.DetectContentType(FileHeader)
|
||||||
FileStat, _ := fd.Stat()
|
FileStat, _ := fd.Stat()
|
||||||
FileSize := strconv.FormatInt(FileStat.Size(), 10)
|
FileSize := strconv.FormatInt(FileStat.Size(), 10)
|
||||||
|
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename=kernel")
|
|
||||||
w.Header().Set("Content-Type", FileContentType)
|
|
||||||
w.Header().Set("Content-Length", FileSize)
|
|
||||||
|
|
||||||
_, err = fd.Seek(0, 0)
|
_, err = fd.Seek(0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return errors.Wrap(err, "failed to seek")
|
return errors.Wrap(err, "failed to seek")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.Copy(w, fd)
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return errors.Wrap(err, "failed to copy")
|
return errors.Wrap(err, "failed to copy")
|
||||||
}
|
}
|
||||||
|
|
||||||
daemonLogf("SEND: %15s: %s\n", sendto, filename)
|
w.Header().Set("Content-Disposition", "attachment; filename=kernel")
|
||||||
|
w.Header().Set("Content-Type", FileContentType)
|
||||||
|
w.Header().Set("Content-Length", FileSize)
|
||||||
|
_, err = buf.WriteTo(w)
|
||||||
|
|
||||||
return nil
|
wwlog.Send("%15s: %s", sendto, filename)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package warewulfd
|
package warewulfd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -17,41 +16,46 @@ import (
|
|||||||
// TODO: https://github.com/pin/tftp
|
// TODO: https://github.com/pin/tftp
|
||||||
|
|
||||||
func RunServer() error {
|
func RunServer() error {
|
||||||
|
err := DaemonInitLogging()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to initialize logging")
|
||||||
|
}
|
||||||
|
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, syscall.SIGHUP)
|
signal.Notify(c, syscall.SIGHUP)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range c {
|
for range c {
|
||||||
daemonLogf("Recieved SIGHUP, reloading...\n")
|
wwlog.Warn("Recieved SIGHUP, reloading...")
|
||||||
err := LoadNodeDB()
|
err := LoadNodeDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not load node DB: %s\n", err)
|
wwlog.Error("Could not load node DB: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = LoadNodeStatus()
|
err = LoadNodeStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not prepopulate node status DB: %s\n", err)
|
wwlog.Error("Could not prepopulate node status DB: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := LoadNodeDB()
|
err = LoadNodeDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("ERROR: Could not load database: %s\n", err)
|
wwlog.Error("Could not load database: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = LoadNodeStatus()
|
err = LoadNodeStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Printf(wwlog.ERROR, "Could not prepopulate node status DB: %s\n", err)
|
wwlog.Error("Could not prepopulate node status DB: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
http.HandleFunc("/ipxe/", IpxeSend)
|
http.HandleFunc("/provision/", ProvisionSend)
|
||||||
http.HandleFunc("/kernel/", KernelSend)
|
http.HandleFunc("/ipxe/", ProvisionSend)
|
||||||
http.HandleFunc("/kmods/", KmodsSend)
|
http.HandleFunc("/kernel/", ProvisionSend)
|
||||||
http.HandleFunc("/container/", ContainerSend)
|
http.HandleFunc("/kmods/", ProvisionSend)
|
||||||
http.HandleFunc("/overlay-system/", SystemOverlaySend)
|
http.HandleFunc("/container/", ProvisionSend)
|
||||||
http.HandleFunc("/overlay-runtime/", RuntimeOverlaySend)
|
http.HandleFunc("/overlay-system/", ProvisionSend)
|
||||||
|
http.HandleFunc("/overlay-runtime/", ProvisionSend)
|
||||||
http.HandleFunc("/status", StatusSend)
|
http.HandleFunc("/status", StatusSend)
|
||||||
|
|
||||||
conf, err := warewulfconf.New()
|
conf, err := warewulfconf.New()
|
||||||
@@ -60,7 +64,7 @@ func RunServer() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
daemonPort := conf.Warewulf.Port
|
daemonPort := conf.Warewulf.Port
|
||||||
daemonLogf("Starting HTTPD REST service on port %d\n", daemonPort)
|
wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort)
|
||||||
|
|
||||||
err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), nil)
|
err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,86 +3,316 @@ package wwlog
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"runtime"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type LogRecord struct {
|
||||||
CRITICAL = 0
|
Level int
|
||||||
SECCRITICAL = 1
|
Err error
|
||||||
ERROR = 2
|
Msg string
|
||||||
SECERROR = 3
|
Args []interface{}
|
||||||
WARN = 4
|
Pc uintptr
|
||||||
SECWARN = 5
|
File string
|
||||||
INFO = 6
|
Line int
|
||||||
SECINFO = 7
|
Time time.Time
|
||||||
VERBOSE = 8
|
}
|
||||||
SECVERBOSE = 9
|
|
||||||
DEBUG = 10
|
/*
|
||||||
SECDEBUG = 11
|
Format a log message from a record
|
||||||
)
|
Only called if rec.level >= logLevel
|
||||||
|
*/
|
||||||
|
type LogFormatter func(logLevel int, rec *LogRecord) string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logLevel = INFO
|
SECCRITICAL = SetLevelName(51, "SECCRITICAL")
|
||||||
|
CRITICAL = SetLevelName(50, "CRITICAL")
|
||||||
|
DENIED = SetLevelName(42, "DENIED")
|
||||||
|
SECERROR = SetLevelName(41, "SECERROR")
|
||||||
|
ERROR = SetLevelName(40, "ERROR")
|
||||||
|
SECWARN = SetLevelName(31, "SECWARN")
|
||||||
|
WARN = SetLevelName(30, "WARN")
|
||||||
|
SEND = SetLevelName(27, "SEND")
|
||||||
|
RECV = SetLevelName(26, "RECV")
|
||||||
|
SERV = SetLevelName(25, "SERV")
|
||||||
|
SECINFO = SetLevelName(21, "SECINFO")
|
||||||
|
INFO = SetLevelName(20, "INFO")
|
||||||
|
SECVERBOSE = SetLevelName(16, "SECVERBOSE")
|
||||||
|
VERBOSE = SetLevelName(15, "VERBOSE")
|
||||||
|
SECDEBUG = SetLevelName(11, "SECDEBUG")
|
||||||
|
DEBUG = SetLevelName(10, "DEBUG")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var levelNums = []int{0}
|
||||||
|
var levelNames = []string{"NOTSET"}
|
||||||
|
var logLevel = INFO
|
||||||
|
var logOut io.Writer = os.Stdout
|
||||||
|
var logErr io.Writer = os.Stderr
|
||||||
|
var logFormatter LogFormatter = DefaultFormatter
|
||||||
|
|
||||||
|
func LevelNameEff(level int) (int, int, string) {
|
||||||
|
n := len(levelNums)
|
||||||
|
idx := sort.SearchInts(levelNums, level)
|
||||||
|
|
||||||
|
if idx >= n {
|
||||||
|
idx = n-1
|
||||||
|
}
|
||||||
|
|
||||||
|
eff_level := levelNums[idx]
|
||||||
|
eff_name := levelNames[idx]
|
||||||
|
|
||||||
|
return idx, eff_level, eff_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func LevelName(level int) string {
|
||||||
|
|
||||||
|
_, _, name := LevelNameEff(level)
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetLevelName(level int, name string) int {
|
||||||
|
n := len(levelNums)
|
||||||
|
idx := sort.SearchInts(levelNums, level)
|
||||||
|
|
||||||
|
if idx < n && levelNums[idx] == level {
|
||||||
|
levelNames[idx] = name
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
levelNums = append(levelNums, level)
|
||||||
|
levelNames = append(levelNames, name)
|
||||||
|
|
||||||
|
if idx < n {
|
||||||
|
copy(levelNums[idx+1:], levelNums[idx:])
|
||||||
|
copy(levelNames[idx+1:], levelNames[idx:])
|
||||||
|
|
||||||
|
levelNums[idx] = level
|
||||||
|
levelNames[idx] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultFormatter(logLevel int, rec *LogRecord) string {
|
||||||
|
message := fmt.Sprintf(rec.Msg, rec.Args...)
|
||||||
|
|
||||||
|
if ( !strings.HasSuffix(message, "\n") ) {
|
||||||
|
// ensure written messages are separated by at least one newline
|
||||||
|
message += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec.Err != nil {
|
||||||
|
if logLevel < VERBOSE {
|
||||||
|
// when debugging errors, add file and line number, and any stack trace
|
||||||
|
message += fmt.Sprintf("%s:%d\n%+v\n", rec.File, rec.Line, rec.Err )
|
||||||
|
|
||||||
|
}else{
|
||||||
|
message += fmt.Sprintf("%v\n", rec.Err )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec.Level == INFO && logLevel == INFO {
|
||||||
|
// NOTE: this is a bit strange, but for user-friendliness it makes sense
|
||||||
|
// to not pollute the messages unless something bad happens (by default).
|
||||||
|
// if logLevel > INFO, then level == INFO messages should not be printed anyway,
|
||||||
|
// and if logLevel < INFO, then it seems like all messages should get prefixed
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
name := LevelName(rec.Level)
|
||||||
|
|
||||||
|
if len(name) <= 7 {
|
||||||
|
return fmt.Sprintf("%-7s: %s", name, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%-11s: %s", name, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func EnabledForLevel(level int) bool {
|
||||||
|
return level >= logLevel
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set the central log level. Uneven values are security related
|
Set the central log level. Uneven values are security related
|
||||||
lof entries.
|
lof entries.
|
||||||
*/
|
*/
|
||||||
func SetLevel(level int) {
|
func SetLogLevel(level int) {
|
||||||
logLevel = level
|
logLevel = level
|
||||||
|
|
||||||
Printf(DEBUG, "Set log level to: %d\n", logLevel)
|
Debug("Set log level to: %d, %s", logLevel, LevelName(logLevel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogLevel() int {
|
||||||
|
return logLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
generate the prefix for log level
|
Set the log output writers
|
||||||
|
By default they are set to os.Stdout and os.Stderr
|
||||||
*/
|
*/
|
||||||
func prefixGen(level int) string {
|
func SetLogWriters(out io.Writer, err io.Writer) {
|
||||||
switch level {
|
logOut = out
|
||||||
case DEBUG:
|
logErr = err
|
||||||
return "[DEBUG] : "
|
Debug("Set log writers")
|
||||||
case SECDEBUG:
|
|
||||||
return "[SECDEBUG] : "
|
|
||||||
case VERBOSE:
|
|
||||||
return "[VERBOSE] : "
|
|
||||||
case SECVERBOSE:
|
|
||||||
return "[SECVERBOSE] : "
|
|
||||||
case INFO:
|
|
||||||
return "[INFO] : "
|
|
||||||
case SECINFO:
|
|
||||||
return "[SECINFO] : "
|
|
||||||
case WARN:
|
|
||||||
return "[WARNING] : "
|
|
||||||
case SECWARN:
|
|
||||||
return "[SECWARNING] : "
|
|
||||||
case ERROR:
|
|
||||||
return "[ERROR] : "
|
|
||||||
case SECERROR:
|
|
||||||
return "[SECERROR] : "
|
|
||||||
case CRITICAL:
|
|
||||||
return "[CRITICAL] : "
|
|
||||||
case SECCRITICAL:
|
|
||||||
return "[SECCRITICAL]: "
|
|
||||||
}
|
|
||||||
return "[UNDEF] : "
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func printlog(level int, message string) {
|
func GetLogWriters() (io.Writer, io.Writer) {
|
||||||
if level == INFO && logLevel <= INFO {
|
return logOut, logErr
|
||||||
fmt.Print(message)
|
}
|
||||||
} else if level <= logLevel {
|
|
||||||
if level < INFO {
|
/*
|
||||||
fmt.Fprintf(os.Stderr, prefixGen(level)+message)
|
Set the log record formatter
|
||||||
|
By default this is set to DefaultFormatter
|
||||||
|
*/
|
||||||
|
func SetLogFormatter(formatter LogFormatter) {
|
||||||
|
logFormatter = formatter
|
||||||
|
Debug("Set log formatter: %s", runtime.FuncForPC(reflect.ValueOf(formatter).Pointer()).Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogFormatter() LogFormatter {
|
||||||
|
return logFormatter
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal method to create a log record
|
||||||
|
*/
|
||||||
|
func LogCaller(level int, skip int, err error, message string, a ...interface{}) {
|
||||||
|
|
||||||
|
if EnabledForLevel(level) {
|
||||||
|
pc, file, line, ok := runtime.Caller(skip + 1)
|
||||||
|
if !ok {
|
||||||
|
file = "[unknown]"
|
||||||
|
}
|
||||||
|
|
||||||
|
rec := LogRecord{
|
||||||
|
Level : level,
|
||||||
|
Err : err,
|
||||||
|
Msg : message,
|
||||||
|
Args : a,
|
||||||
|
Pc : pc,
|
||||||
|
File : file,
|
||||||
|
Line : line,
|
||||||
|
Time : time.Now() }
|
||||||
|
|
||||||
|
message = logFormatter(logLevel, &rec)
|
||||||
|
|
||||||
|
if level >= ERROR {
|
||||||
|
fmt.Fprint(logErr, message)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(prefixGen(level) + message)
|
fmt.Fprint(logOut, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Println(level int, message string) {
|
func Println(level int, message string) {
|
||||||
printlog(level, message)
|
LogCaller(level, 1, nil, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Printf(level int, message string, a ...interface{}) {
|
func Printf(level int, message string, a ...interface{}) {
|
||||||
printlog(level, fmt.Sprintf(message, a...))
|
LogCaller(level, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
Named log level functions
|
||||||
|
*/
|
||||||
|
func Log(level int, message string, a ...interface{}) {
|
||||||
|
LogCaller(level, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogExc(level int, err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(level, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(message string, a ...interface{}) {
|
||||||
|
LogCaller(DEBUG, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DebugExc(err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(DEBUG, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecDebug(message string, a ...interface{}) {
|
||||||
|
LogCaller(SECDEBUG, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Verbose(message string, a ...interface{}) {
|
||||||
|
LogCaller(VERBOSE, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerboseExc(err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(VERBOSE, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecVerbose(message string, a ...interface{}) {
|
||||||
|
LogCaller(SECVERBOSE, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(message string, a ...interface{}) {
|
||||||
|
LogCaller(INFO, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InfoExc(err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(INFO, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecInfo(message string, a ...interface{}) {
|
||||||
|
LogCaller(SECINFO, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serv(message string, a ...interface{}) {
|
||||||
|
LogCaller(SERV, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Recv(message string, a ...interface{}) {
|
||||||
|
LogCaller(RECV, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Send(message string, a ...interface{}) {
|
||||||
|
LogCaller(SEND, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warn(message string, a ...interface{}) {
|
||||||
|
LogCaller(WARN, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WarnExc(err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(WARN, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecWarn(message string, a ...interface{}) {
|
||||||
|
LogCaller(SECWARN, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(message string, a ...interface{}) {
|
||||||
|
LogCaller(ERROR, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorExc(err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(ERROR, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecError(message string, a ...interface{}) {
|
||||||
|
LogCaller(SECERROR, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Denied(message string, a ...interface{}) {
|
||||||
|
LogCaller(DENIED, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Critical(message string, a ...interface{}) {
|
||||||
|
LogCaller(CRITICAL, 1, nil, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CriticalExc(err error, message string, a ...interface{}) {
|
||||||
|
LogCaller(CRITICAL, 1, err, message, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecCritical(message string, a ...interface{}) {
|
||||||
|
LogCaller(SECCRITICAL, 1, nil, message, a...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
{{if .Dhcp.Enabled -}}
|
{{if .Dhcp.Enabled -}}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
# Host: {{.BuildHost}}
|
||||||
# Time: {{.BuildTime}}
|
# Time: {{.BuildTime}}
|
||||||
# Source: {{.BuildSource}}
|
# Source: {{.BuildSource}}
|
||||||
|
|
||||||
allow booting;
|
allow booting;
|
||||||
allow bootp;
|
allow bootp;
|
||||||
ddns-update-style interim;
|
ddns-update-style interim;
|
||||||
@@ -40,15 +41,22 @@ subnet {{$.Network}} netmask {{$.Netmask}} {
|
|||||||
next-server {{$.Ipaddr}};
|
next-server {{$.Ipaddr}};
|
||||||
}
|
}
|
||||||
{{range $nodes := .AllNodes}}
|
{{range $nodes := .AllNodes}}
|
||||||
{{- range $netdevs := $nodes.NetDevs}}
|
{{- range $netname, $netdevs := $nodes.NetDevs}}
|
||||||
{{- if $netdevs.Primary.Get}}
|
host {{$nodes.Id.Get}}-{{if $netdevs.Device.Defined}}{{$netdevs.Device.Get}}{{else}}{{$netname}}{{end}} {
|
||||||
host {{$nodes.Id.Get}} {
|
{{- if $netdevs.Hwaddr.Defined}}
|
||||||
hardware ethernet {{$netdevs.Hwaddr.Get}};
|
hardware ethernet {{$netdevs.Hwaddr.Get}};
|
||||||
fixed-address {{$netdevs.Ipaddr.Get}};
|
{{- end}}
|
||||||
|
{{- if $netdevs.Ipaddr.Defined}}
|
||||||
|
fixed-address {{$netdevs.Ipaddr.Get}};
|
||||||
|
{{- end}}
|
||||||
|
{{- if $netdevs.Primary.GetB}}
|
||||||
|
option host-name "{{$nodes.Id.Get}}";
|
||||||
|
{{else}}
|
||||||
|
option host-name "{{$nodes.Id.Get}}-{{if $netdevs.Device.Defined}}{{$netdevs.Device.Get}}{{else}}{{$netname}}{{end}}";
|
||||||
|
{{- end}}
|
||||||
}
|
}
|
||||||
{{end -}}
|
{{end -}}
|
||||||
{{end -}}
|
{{end -}}
|
||||||
{{end -}}
|
|
||||||
|
|
||||||
{{else -}}
|
{{else -}}
|
||||||
subnet {{$.Network}} netmask {{$.Netmask}} {
|
subnet {{$.Network}} netmask {{$.Netmask}} {
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
{{ IncludeBlock "/etc/hosts" "# Do not edit after this line" }}
|
{{ IncludeBlock "/etc/hosts" "# Do not edit after this line" }}
|
||||||
# Do not edit after this line
|
# This block is autogenerated by warewulf
|
||||||
# This block is autogenerated by warewulf
|
|
||||||
# Host: {{.BuildHost}}
|
# Host: {{.BuildHost}}
|
||||||
# Time: {{.BuildTime}}
|
# Time: {{.BuildTime}}
|
||||||
# Source: {{.BuildSource}}
|
# Source: {{.BuildSource}}
|
||||||
|
|
||||||
|
# Warewulf Server
|
||||||
|
{{$.Ipaddr}} {{$.BuildHost}}
|
||||||
{{range $node := $.AllNodes}} {{/* for each node */}}
|
{{range $node := $.AllNodes}} {{/* for each node */}}
|
||||||
# Entry for {{$node.Id.Get}}
|
# Entry for {{$node.Id.Get}}
|
||||||
{{- range $devname, $netdev := $node.NetDevs}} {{/* for each network device on the node */}}
|
{{- range $devname, $netdev := $node.NetDevs}} {{/* for each network device on the node */}}
|
||||||
{{- if $netdev.Ipaddr.Defined}} {{/* if we have an ip address on this network device */}}
|
{{- if $netdev.Ipaddr.Defined}} {{/* if we have an ip address on this network device */}}
|
||||||
{{- if $netdev.Primary.GetB}} {{/* emit the node name as hostname if this is the default */}}
|
{{- /* emit the node name as hostname if this is the primary */}}
|
||||||
{{$netdev.Ipaddr.Get}} {{$node.Id.Get}} {{$node.Id.Get}}-{{$devname}} {{$node.Id.Get}}-{{$netdev.Device.Get}}
|
{{$netdev.Ipaddr.Get}} {{$node.Id.Get}}-{{$devname}}
|
||||||
{{- else}}
|
{{- if $netdev.Device.Defined}} {{$node.Id.Get}}-{{$netdev.Device.Get}}{{end}}
|
||||||
{{$netdev.Ipaddr.Get}} {{$node.Id.Get}}-{{$devname}} {{$node.Id.Get}}-{{$netdev.Device.Get}}
|
{{- if $netdev.Primary.GetB}} {{$node.Id.Get}}{{end}}
|
||||||
{{- end}} {{/* end if default */}}
|
|
||||||
{{- end}} {{/* end if ip */}}
|
{{- end}} {{/* end if ip */}}
|
||||||
{{- end}} {{/* end for each network device */}}
|
{{- end}} {{/* end for each network device */}}
|
||||||
{{end}} {{/* end for each node */}}
|
{{- end}} {{/* end for each node */}}
|
||||||
|
|||||||
Reference in New Issue
Block a user