Refactor of server, logging, building of images, template updates
* Combine provision response handling, add hwaddr and interface to dhcp * Simplify stage cases, add backward compatible uri parse * Move control of image compression to client * Fix kernel search path for ubuntu, buffer template generation * Implement cpio and gz as functions, add log named functions * Generalize fs image creation routine * Combine daemonLogf to generalized wwlog * Fix template newline insertion, update dhcp and hosts tmpl * Update IPXE template to handle non-compressed files * Update DHCP template to set network interfaces and server IP assignment * Update DHCP/hosts templates to choose a host-name self-consistently
This commit is contained in:
3
Makefile
3
Makefile
@@ -154,7 +154,7 @@ test-cover: ## Run test coverage and generate html report
|
||||
rm *.coverprofile
|
||||
go tool cover -html=coverage/cover.out -o=coverage/cover.html
|
||||
|
||||
debian: all
|
||||
debian: all
|
||||
|
||||
files: all
|
||||
install -d -m 0755 $(DESTDIR)$(BINDIR)
|
||||
@@ -265,4 +265,3 @@ clean:
|
||||
install: files install_wwclient
|
||||
|
||||
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 Warewulf v4 now booting: {{.Fqdn}}
|
||||
echo Warewulf v4 now booting: {{.Fqdn}} ({{.Hwaddr}})
|
||||
echo
|
||||
echo Container: {{.ContainerName}}
|
||||
{{if .KernelOverride }}
|
||||
@@ -13,14 +13,68 @@ echo Kernel: {{.ContainerName}} (container default)
|
||||
echo KernelArgs: {{.KernelArgs}}
|
||||
echo
|
||||
|
||||
set base http://{{.Ipaddr}}:{{.Port}}
|
||||
{{if ne .Syslog "" -}}
|
||||
set syslog {{$.Syslog}};
|
||||
{{- end}}
|
||||
{{if ne .Syslogs "" -}}
|
||||
set syslogs {{$.Syslogs}};
|
||||
{{- end}}
|
||||
|
||||
kernel --name kernel ${base}/kernel/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || goto reboot
|
||||
initrd --name container ${base}/container/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid} || 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
|
||||
set uri_base http://{{.Ipaddr}}:{{.Port}}/provision/{{.Hwaddr}}?assetkey=${asset}&uuid=${uuid}
|
||||
echo Downloading node provisions: ${uri_base}
|
||||
|
||||
echo Kernel image:
|
||||
kernel --name kernel ${uri_base}&stage=kernel || 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
|
||||
|
||||
echo Runtime image:
|
||||
imgextract --name runtime ${uri_base}&stage=runtime&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
|
||||
|
||||
echo Runtime image:
|
||||
initrd --name runtime ${uri_base}&stage=runtime || 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 initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||
sleep 3
|
||||
boot kernel initrd=container initrd=kmods initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||
{{- else -}}
|
||||
echo boot kernel initrd=container initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}}
|
||||
sleep 3
|
||||
boot kernel initrd=container initrd=system initrd=runtime wwid={{.Hwaddr}} {{.KernelArgs}} || goto reboot
|
||||
{{- end}}
|
||||
|
||||
:reboot
|
||||
echo
|
||||
|
||||
@@ -178,7 +178,7 @@ func updateSystem(ipaddr string, port int, wwid string, tag string, localUUID uu
|
||||
counter := 0
|
||||
for {
|
||||
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)
|
||||
resp, err = Webclient.Get(getString)
|
||||
if err == nil {
|
||||
|
||||
@@ -75,11 +75,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
name = args[1]
|
||||
} else {
|
||||
name = path.Base(uri)
|
||||
fmt.Printf("Setting VNFS name: %s\n", name)
|
||||
wwlog.Info("Setting VNFS name: %s", 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)
|
||||
}
|
||||
|
||||
@@ -87,75 +87,75 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if util.IsDir(fullPath) {
|
||||
if SetForce {
|
||||
fmt.Printf("Overwriting existing VNFS\n")
|
||||
wwlog.Info("Overwriting existing VNFS")
|
||||
err := os.RemoveAll(fullPath)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if SetUpdate {
|
||||
fmt.Printf("Updating existing VNFS\n")
|
||||
wwlog.Info("Updating existing VNFS")
|
||||
} 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)
|
||||
}
|
||||
} else if strings.HasPrefix(uri, "docker://") || strings.HasPrefix(uri, "docker-daemon://") ||
|
||||
strings.HasPrefix(uri, "file://") || util.IsFile(uri) {
|
||||
sCtx, err := getSystemContext()
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
}
|
||||
|
||||
err = container.ImportDocker(uri, name, sCtx)
|
||||
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)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if util.IsDir(uri) {
|
||||
err := container.ImportDirectory(uri, name)
|
||||
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)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
wwlog.Printf(wwlog.ERROR, "Invalid dir or uri: %s\n", uri)
|
||||
wwlog.Error("Invalid dir or uri: %s", uri)
|
||||
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"))
|
||||
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, !NoSyncUser)
|
||||
if err != nil && !NoSyncUser {
|
||||
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)
|
||||
}
|
||||
|
||||
fmt.Printf("Building container: %s\n", name)
|
||||
wwlog.Info("Building container: %s", name)
|
||||
err = container.Build(name, true)
|
||||
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)
|
||||
}
|
||||
|
||||
if SetDefault {
|
||||
nodeDB, err := node.New()
|
||||
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)
|
||||
}
|
||||
|
||||
//TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map
|
||||
profiles, _ := nodeDB.FindAllProfiles()
|
||||
for _, profile := range profiles {
|
||||
wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get())
|
||||
wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s", profile.Id.Get())
|
||||
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)
|
||||
err := nodeDB.ProfileUpdate(profile)
|
||||
if err != nil {
|
||||
@@ -168,7 +168,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
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()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to reload warewulf daemon")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package add
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"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
|
||||
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 {
|
||||
if DebugFlag {
|
||||
wwlog.SetLevel(wwlog.DEBUG)
|
||||
wwlog.SetLogLevel(wwlog.DEBUG)
|
||||
} else if verboseArg {
|
||||
wwlog.SetLevel(wwlog.VERBOSE)
|
||||
wwlog.SetLogLevel(wwlog.VERBOSE)
|
||||
} else {
|
||||
wwlog.SetLevel(wwlog.INFO)
|
||||
wwlog.SetLogLevel(wwlog.INFO)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -18,50 +16,42 @@ func Build(name string, buildForce bool) error {
|
||||
imagePath := ImageFile(name)
|
||||
|
||||
if !ValidSource(name) {
|
||||
return errors.New("Container does not exist")
|
||||
return errors.Errorf("Container does not exist: %s", name)
|
||||
}
|
||||
|
||||
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) {
|
||||
wwlog.Printf(wwlog.INFO, "Skipping (VNFS is current)\n")
|
||||
wwlog.Info("Skipping (VNFS is current)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", name)
|
||||
err := os.MkdirAll(path.Dir(imagePath), 0755)
|
||||
if err != nil {
|
||||
return errors.New("Failed creating directory")
|
||||
excludes_file := path.Join(rootfsPath, "./etc/warewulf/excludes")
|
||||
ignore := []string{}
|
||||
|
||||
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 = os.MkdirAll(path.Dir(rootfsPath), 0755)
|
||||
if err != nil {
|
||||
return errors.New("Failed creating directory")
|
||||
}
|
||||
err := util.BuildFsImage(
|
||||
"VNFS container " + name,
|
||||
rootfsPath,
|
||||
imagePath,
|
||||
[]string{"*"},
|
||||
ignore,
|
||||
// ignore cross-device files
|
||||
true,
|
||||
"newc")
|
||||
|
||||
compressor, err := exec.LookPath("pigz")
|
||||
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
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ func ImageParentDir() 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{
|
||||
`vmlinux`,
|
||||
`vmlinuz`,
|
||||
`vmlinuz.gz`,
|
||||
}
|
||||
modulePath = "/lib/modules/"
|
||||
`vmlinux-*`,
|
||||
`vmlinuz-*`,
|
||||
`vmlinuz.gz` }
|
||||
|
||||
kernelDirs = []string{
|
||||
`/lib/modules/*/`,
|
||||
`/boot/` }
|
||||
)
|
||||
|
||||
func KernelFind(container string) string {
|
||||
@@ -25,21 +29,28 @@ func KernelFind(container string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, kname := range kernelNames {
|
||||
wwlog.Printf(wwlog.DEBUG, "Checking for kernel name within module path: %s\n", kname)
|
||||
kernelPaths, err := filepath.Glob(path.Join(container_path, modulePath, "/*/", kname))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, kdir := range kernelDirs {
|
||||
wwlog.Printf(wwlog.DEBUG, "Checking kernel directory: %s\n", kdir)
|
||||
for _, kname := range kernelNames {
|
||||
wwlog.Printf(wwlog.DEBUG, "Checking for kernel name: %s\n", kname)
|
||||
kernelPaths, err := filepath.Glob(path.Join(container_path, kdir, kname))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
sort.Slice(kernelPaths, func(i, j int) bool {
|
||||
return kernelPaths[i] > kernelPaths[j]
|
||||
})
|
||||
if len(kernelPaths) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, kernelPath := range kernelPaths {
|
||||
wwlog.Printf(wwlog.DEBUG, "Checking for kernel path: %s\n", kernelPath)
|
||||
if util.IsFile(kernelPath) {
|
||||
return kernelPath
|
||||
sort.Slice(kernelPaths, func(i, j int) bool {
|
||||
return kernelPaths[i] > kernelPaths[j]
|
||||
})
|
||||
|
||||
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/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -35,12 +34,12 @@ func KernelImageTopDir() string {
|
||||
|
||||
func KernelImage(kernelName string) string {
|
||||
if kernelName == "" {
|
||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
||||
wwlog.Error("Kernel Name is not defined")
|
||||
return ""
|
||||
}
|
||||
|
||||
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 ""
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ func KernelImage(kernelName string) string {
|
||||
|
||||
func GetKernelVersion(kernelName string) string {
|
||||
if kernelName == "" {
|
||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
||||
wwlog.Error("Kernel Name is not defined")
|
||||
return ""
|
||||
}
|
||||
kernelVersion, err := ioutil.ReadFile(KernelVersionFile(kernelName))
|
||||
@@ -61,12 +60,12 @@ func GetKernelVersion(kernelName string) string {
|
||||
|
||||
func KmodsImage(kernelName string) string {
|
||||
if kernelName == "" {
|
||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
||||
wwlog.Error("Kernel Name is not defined")
|
||||
return ""
|
||||
}
|
||||
|
||||
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 ""
|
||||
}
|
||||
|
||||
@@ -75,12 +74,12 @@ func KmodsImage(kernelName string) string {
|
||||
|
||||
func KernelVersionFile(kernelName string) string {
|
||||
if kernelName == "" {
|
||||
wwlog.Printf(wwlog.ERROR, "Kernel Name is not defined\n")
|
||||
wwlog.Error("Kernel Name is not defined")
|
||||
return ""
|
||||
}
|
||||
|
||||
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 ""
|
||||
}
|
||||
|
||||
@@ -95,7 +94,7 @@ func ListKernels() ([]string, error) {
|
||||
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())
|
||||
if err != nil {
|
||||
@@ -103,7 +102,7 @@ func ListKernels() ([]string, error) {
|
||||
}
|
||||
|
||||
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())
|
||||
|
||||
@@ -113,8 +112,8 @@ func ListKernels() ([]string, error) {
|
||||
}
|
||||
|
||||
func Build(kernelVersion, kernelName, root string) (string, error) {
|
||||
kernelDrivers := path.Join(root, "/lib/modules/", kernelVersion)
|
||||
kernelDriversRelative := path.Join("/lib/modules/", kernelVersion)
|
||||
kernelDrivers := path.Join(root, kernelDriversRelative)
|
||||
kernelDestination := KernelImage(kernelName)
|
||||
driversDestination := KmodsImage(kernelName)
|
||||
versionDestination := KernelVersionFile(kernelName)
|
||||
@@ -138,7 +137,7 @@ func Build(kernelVersion, kernelName, root string) (string, error) {
|
||||
|
||||
for _, searchPath := range kernelSearchPaths {
|
||||
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) {
|
||||
kernelSource = testPath
|
||||
break
|
||||
@@ -146,17 +145,17 @@ func Build(kernelVersion, kernelName, root string) (string, error) {
|
||||
}
|
||||
|
||||
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")
|
||||
} else {
|
||||
wwlog.Printf(wwlog.INFO, "Found kernel at: %s\n", kernelSource)
|
||||
wwlog.Info("Found kernel at: %s", kernelSource)
|
||||
}
|
||||
|
||||
if !util.IsDir(kernelDrivers) {
|
||||
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 {
|
||||
kernel, err := os.Open(kernelSource)
|
||||
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 {
|
||||
compressor, err := exec.LookPath("pigz")
|
||||
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)
|
||||
}
|
||||
name := kernelName + " drivers"
|
||||
wwlog.Verbose("Creating image for %s: %s", name, root)
|
||||
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
wwlog.Printf(wwlog.VERBOSE, "Creating version file\n")
|
||||
wwlog.Verbose("Creating version file")
|
||||
file, err := os.Create(versionDestination)
|
||||
if err != nil {
|
||||
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 {
|
||||
fullPath := path.Join(KernelImageTopDir(), name)
|
||||
|
||||
wwlog.Printf(wwlog.VERBOSE, "Removing path: %s\n", fullPath)
|
||||
wwlog.Verbose("Removing path: %s", fullPath)
|
||||
return os.RemoveAll(fullPath)
|
||||
}
|
||||
|
||||
func FindKernelVersion(root string) (string, error) {
|
||||
for _, searchPath := range kernelSearchPaths {
|
||||
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)
|
||||
if len(potentialKernel) == 0 {
|
||||
continue
|
||||
}
|
||||
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-\.]*)`))
|
||||
version := re.FindAllStringSubmatch(foundKernel, -1)
|
||||
if version == nil {
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
@@ -55,6 +55,14 @@ func templateFileBlock(inc string, abortStr string) (string, error) {
|
||||
}
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -51,16 +50,16 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
|
||||
for _, n := range nodes {
|
||||
|
||||
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)
|
||||
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()
|
||||
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)
|
||||
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 {
|
||||
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})
|
||||
if err != nil {
|
||||
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)
|
||||
var idEntry node.Entry
|
||||
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)
|
||||
host.Id = idEntry
|
||||
hostdir := OverlaySourceDir("host")
|
||||
@@ -100,7 +99,7 @@ func BuildHostOverlay() error {
|
||||
return errors.Wrap(err, "could not build host overlay ")
|
||||
}
|
||||
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"}, "/")
|
||||
}
|
||||
@@ -119,7 +118,7 @@ func FindOverlays() ([]string, error) {
|
||||
}
|
||||
|
||||
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())
|
||||
|
||||
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 {
|
||||
// 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)
|
||||
overlayImageDir := path.Dir(overlayImage)
|
||||
|
||||
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 {
|
||||
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)
|
||||
compressor, err := exec.LookPath("pigz")
|
||||
wwlog.Debug("Created directory for %s: %s", name, overlayImageDir)
|
||||
|
||||
buildDir, err := ioutil.TempDir(os.TempDir(), ".wwctl-overlay-")
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.DEBUG, "Could not locate PIGZ, using GZIP\n")
|
||||
compressor = "gzip"
|
||||
} else {
|
||||
wwlog.Printf(wwlog.DEBUG, "Using PIGZ to compress the overlay: %s\n", compressor)
|
||||
return errors.Wrapf(err, "Failed to create temporary directory for %s", name )
|
||||
}
|
||||
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 = exec.Command("/bin/sh", "-c", cmd).Run()
|
||||
err = BuildOverlayIndir(nodeInfo, overlayNames, buildDir)
|
||||
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)
|
||||
os.RemoveAll(outputDir)
|
||||
return nil
|
||||
wwlog.Debug("Generated files for %s", name)
|
||||
|
||||
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 {
|
||||
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) {
|
||||
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()
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
nodeDB, err := node.New()
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
allNodes, err := nodeDB.FindAllNodes()
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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
|
||||
tstruct.Kernel = new(node.KernelConf)
|
||||
tstruct.Ipmi = new(node.IpmiConf)
|
||||
tstruct.Id = nodeInfo.Id.Get()
|
||||
tstruct.Hostname = nodeInfo.Id.Get()
|
||||
tstruct.Id = nodeInfo.Id.Get()
|
||||
tstruct.Hostname = nodeInfo.Id.Get()
|
||||
tstruct.ClusterName = nodeInfo.ClusterName.Get()
|
||||
tstruct.Container = nodeInfo.ContainerName.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.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
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) {
|
||||
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 {
|
||||
if err != nil {
|
||||
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() {
|
||||
wwlog.Printf(wwlog.DEBUG, "Found directory: %s\n", location)
|
||||
wwlog.Debug("Found directory: %s", location)
|
||||
|
||||
err = os.MkdirAll(path.Join(outputDir, location), info.Mode())
|
||||
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")
|
||||
}
|
||||
|
||||
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" {
|
||||
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")
|
||||
backupFile := true
|
||||
writeFile := true
|
||||
@@ -351,12 +344,12 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
||||
"dec": func(i int) int { return i - 1 },
|
||||
"file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
|
||||
"abort": func() string {
|
||||
wwlog.Printf(wwlog.DEBUG, "abort file called in %s\n", location)
|
||||
wwlog.Debug("abort file called in %s", location)
|
||||
writeFile = false
|
||||
return ""
|
||||
},
|
||||
"nobackup": func() string {
|
||||
wwlog.Printf(wwlog.DEBUG, "not backup for %s\n", location)
|
||||
wwlog.Debug("not backup for %s", location)
|
||||
backupFile = false
|
||||
return ""
|
||||
},
|
||||
@@ -372,7 +365,6 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
||||
err = tmpl.Execute(&buffer, tstruct)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not execute template")
|
||||
|
||||
}
|
||||
if writeFile {
|
||||
destFileName := destFile
|
||||
@@ -386,7 +378,7 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
||||
line := fileScanner.Text()
|
||||
filenameFromTemplate := reg.FindAllStringSubmatch(line, -1)
|
||||
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 {
|
||||
err = carefulWriteBuffer(path.Join(outputDir, destFileName),
|
||||
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")
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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 {
|
||||
wwlog.Printf(wwlog.DEBUG, "Found symlink %s\n", location)
|
||||
wwlog.Debug("Found symlink %s", location)
|
||||
destination, err := os.Readlink(location)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
}
|
||||
err = os.Symlink(destination, path.Join(outputDir, location))
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.ERROR, "%s\n", err)
|
||||
wwlog.ErrorExc(err, "")
|
||||
}
|
||||
} else {
|
||||
|
||||
err := util.CopyFile(location, path.Join(outputDir, location))
|
||||
if err == nil {
|
||||
wwlog.Printf(wwlog.DEBUG, "Copied file into overlay: %s\n", location)
|
||||
wwlog.Debug("Copied file into overlay: %s", location)
|
||||
} else {
|
||||
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.
|
||||
*/
|
||||
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 !util.IsFile(path.Join(outputDir, destFile+".wwbackup")) && util.IsFile(path.Join(outputDir, destFile)) {
|
||||
if !util.IsFile(destFile+".wwbackup") && util.IsFile(destFile) {
|
||||
err := util.CopyFile(destFile, destFile+".wwbackup")
|
||||
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"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -45,17 +47,17 @@ func DirModTime(path string) (time.Time, error) {
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.DEBUG, "%s\n", err)
|
||||
wwlog.DebugExc(err, "")
|
||||
return false
|
||||
}
|
||||
|
||||
time2, err := DirModTime(compare)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.DEBUG, "%s\n", err)
|
||||
wwlog.DebugExc(err, "")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ func RandomString(n int) string {
|
||||
}
|
||||
|
||||
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 == "" {
|
||||
return false
|
||||
@@ -85,7 +87,7 @@ func IsDir(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 == "" {
|
||||
return false
|
||||
@@ -120,18 +122,19 @@ func ValidString(pattern string, expr string) bool {
|
||||
|
||||
func ValidateOrDie(message string, pattern string, expr string) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
func FindFiles(path string) []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)
|
||||
if err != nil {
|
||||
wwlog.Printf(wwlog.WARN, "Could not chdir() to: %s\n", path)
|
||||
wwlog.Warn("Could not chdir() to: %s", path)
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -145,10 +148,10 @@ func FindFiles(path string) []string {
|
||||
}
|
||||
|
||||
if IsDir(location) {
|
||||
wwlog.Printf(wwlog.DEBUG, "FindFiles() found directory: %s\n", location)
|
||||
wwlog.Debug("FindFiles() found directory: %s", location)
|
||||
ret = append(ret, location+"/")
|
||||
} else {
|
||||
wwlog.Printf(wwlog.DEBUG, "FindFiles() found file: %s\n", location)
|
||||
wwlog.Debug("FindFiles() found file: %s", location)
|
||||
ret = append(ret, location)
|
||||
}
|
||||
|
||||
@@ -161,8 +164,123 @@ func FindFiles(path string) []string {
|
||||
return ret
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
func FindFilterFiles(
|
||||
path string,
|
||||
include []string,
|
||||
ignore []string,
|
||||
ignore_xdev bool) ([]string, error) {
|
||||
|
||||
wwlog.Debug("Finding files: %s", path)
|
||||
|
||||
ofiles := []string{}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return ofiles, err
|
||||
}
|
||||
defer 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 {
|
||||
if stat, err := os.Stat(ifile); err == nil && 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 {
|
||||
wwlog.Printf(wwlog.DEBUG, "ExecInteractive(%s, %s)\n", command, a)
|
||||
wwlog.Debug("ExecInteractive(%s, %s)", command, a)
|
||||
c := exec.Command(command, a...)
|
||||
c.Stdin = os.Stdin
|
||||
c.Stdout = os.Stdout
|
||||
@@ -196,7 +314,7 @@ func SliceRemoveElement(array []string, remove string) []string {
|
||||
if r != remove {
|
||||
ret = append(ret, r)
|
||||
} else {
|
||||
wwlog.Printf(wwlog.DEBUG, "Removing slice from array: %s\n", remove)
|
||||
wwlog.Debug("Removing slice from array: %s", remove)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +358,7 @@ func SystemdStart(systemdName string) error {
|
||||
startCmd := fmt.Sprintf("systemctl restart %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)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to run start cmd")
|
||||
@@ -266,7 +384,7 @@ func CopyUIDGID(source string, dest string) error {
|
||||
UID = int(stat.Uid)
|
||||
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)
|
||||
return err
|
||||
}
|
||||
@@ -330,18 +448,161 @@ func IncrementIPv4(start string, inc uint) string {
|
||||
Appending the lines to the given file
|
||||
*/
|
||||
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)
|
||||
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()
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Create an archive using cpio
|
||||
*/
|
||||
func CpioCreate(
|
||||
ifiles []string,
|
||||
ofile string,
|
||||
format string,
|
||||
cpio_args ...string ) 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
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer stdin.Close()
|
||||
io.WriteString(stdin, strings.Join(ifiles, "\n"))
|
||||
}()
|
||||
|
||||
out, err := proc.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
wwlog.Debug(string(out))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Compress a file using gzip or pigz
|
||||
*/
|
||||
func FileGz(
|
||||
file string ) 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: ", 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 ) 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 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
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ type DhcpConf struct {
|
||||
RangeStart string `yaml:"range start" default:"192.168.200.50"`
|
||||
RangeEnd string `yaml:"range end" default:"192.168.200.99"`
|
||||
SystemdName string `yaml:"systemd name" default:"dhcpd"`
|
||||
Syslog string `yaml:"syslog" default:""`
|
||||
Syslogs string `yaml:"syslogs" default:""`
|
||||
Interface string `yaml:"interface" default:""`
|
||||
Hwaddr string `yaml:"hwaddr" default:""`
|
||||
}
|
||||
|
||||
type TftpConf struct {
|
||||
|
||||
@@ -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 (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
"github.com/hpcng/warewulf/internal/pkg/version"
|
||||
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -22,37 +22,50 @@ const (
|
||||
WAREWULFD_LOGFILE = "/var/log/warewulfd.log"
|
||||
)
|
||||
|
||||
var logwriter *syslog.Writer
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
conf, err := warewulfconf.New()
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: Could not read Warewulf configuration file: %s\n", err)
|
||||
return
|
||||
return errors.Wrap(err, "Could not read Warewulf configuration file")
|
||||
}
|
||||
|
||||
if conf.Warewulf.Syslog {
|
||||
if !loginit {
|
||||
var err error
|
||||
|
||||
logwriter, err = syslog.New(syslog.LOG_NOTICE, "warewulfd")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.SetOutput(logwriter)
|
||||
loginit = true
|
||||
wwlog.Debug("Changingq log output to syslog")
|
||||
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("")
|
||||
logwriter, err := syslog.New(syslog.LOG_NOTICE, "warewulfd")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Could not create syslog writer")
|
||||
}
|
||||
|
||||
log.Printf(message, a...)
|
||||
wwlog.SetLogWriters(logwriter, logwriter)
|
||||
|
||||
wwlog.SetLogFormatter(wwlog.DefaultFormatter)
|
||||
|
||||
} else {
|
||||
prefix := fmt.Sprintf("[%s] ", time.Now().Format(time.UnixDate))
|
||||
fmt.Printf(prefix+message, a...)
|
||||
}
|
||||
|
||||
loginit = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DaemonStart() error {
|
||||
@@ -68,6 +81,7 @@ func DaemonStart() error {
|
||||
}
|
||||
|
||||
os.Setenv("WAREWULFD_BACKGROUND", "1")
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.GetLogLevel()))
|
||||
|
||||
f, err := os.OpenFile(WAREWULFD_LOGFILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
@@ -91,7 +105,7 @@ func DaemonStart() error {
|
||||
|
||||
fmt.Fprintf(p, "%d", pid)
|
||||
|
||||
fmt.Printf("Started Warewulf (%s) server at PID: %d\n", version.GetVersion(), pid)
|
||||
wwlog.Info("Started Warewulf (%s) server at PID: %d", version.GetVersion(), pid)
|
||||
|
||||
}
|
||||
|
||||
@@ -117,7 +131,7 @@ func DaemonStatus() error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to send process SIGCONT")
|
||||
} else {
|
||||
fmt.Printf("Warewulf server is running at PID: %d\n", pid)
|
||||
wwlog.Info("Warewulf server is running at PID: %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,12 +159,14 @@ func DaemonReload() error {
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("WAREWULFD_LOGLEVEL", strconv.Itoa(wwlog.GetLogLevel()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DaemonStop() error {
|
||||
if !util.IsFile(WAREWULFD_PIDFILE) {
|
||||
fmt.Printf("Warewulf daemon process not running\n")
|
||||
wwlog.Warn("Warewulf daemon process not running")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -171,7 +187,7 @@ func DaemonStop() error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to send process SIGTERM")
|
||||
} else {
|
||||
fmt.Printf("Terminated Warewulf server at PID: %d\n", pid)
|
||||
wwlog.Info("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
|
||||
assetkey string
|
||||
uuid string
|
||||
stage string
|
||||
overlay string
|
||||
compress string
|
||||
}
|
||||
|
||||
func parseReq(req *http.Request) (parserInfo, error) {
|
||||
var ret parserInfo
|
||||
|
||||
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.ToLower(hwaddr)
|
||||
|
||||
@@ -36,9 +46,35 @@ func parseReq(req *http.Request) (parserInfo, error) {
|
||||
if len(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 {
|
||||
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 == "" {
|
||||
return ret, errors.New("no hwaddr encoded in GET")
|
||||
}
|
||||
|
||||
279
internal/pkg/warewulfd/provision.go
Normal file
279
internal/pkg/warewulfd/provision.go
Normal file
@@ -0,0 +1,279 @@
|
||||
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
|
||||
Syslog string
|
||||
Syslogs 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
|
||||
}
|
||||
|
||||
if conf.Warewulf.Secure {
|
||||
if rinfo.remoteport >= 1024 {
|
||||
wwlog.Error("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" }
|
||||
|
||||
wwlog.Info("REQ: %s: %s", rinfo.hwaddr, rinfo.stage )
|
||||
|
||||
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.Info("%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.Info("%s (failed to set node configuration)", rinfo.hwaddr)
|
||||
|
||||
} else {
|
||||
err := nodeDB.Persist()
|
||||
if err != nil {
|
||||
wwlog.Info("%s (failed to persist node configuration)", rinfo.hwaddr)
|
||||
|
||||
} else {
|
||||
node = n
|
||||
_ = overlay.BuildAllOverlays([]nodepkg.NodeInfo{n})
|
||||
|
||||
wwlog.Info("%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.Error("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),
|
||||
Syslog : conf.Dhcp.Syslog,
|
||||
Syslogs : conf.Dhcp.Syslogs,
|
||||
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.Info("BUILD %15s, overlays %v", node.Id.Get(), stage_overlays)
|
||||
_ = overlay.BuildOverlay(node, stage_overlays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wwlog.Info("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()))
|
||||
buf.WriteTo(w)
|
||||
|
||||
wwlog.Info("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) {
|
||||
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
|
||||
n.NodeName = nodeID
|
||||
@@ -71,7 +71,7 @@ func updateStatus(nodeID, stage, sent, ipaddr string) {
|
||||
|
||||
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, "", " ")
|
||||
if err != nil {
|
||||
@@ -85,11 +85,12 @@ func StatusSend(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
status, err := statusJSON()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(status)
|
||||
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 (
|
||||
"io"
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func sendFile(w http.ResponseWriter, filename string, sendto string) error {
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
@@ -19,27 +22,33 @@ func sendFile(w http.ResponseWriter, filename string, sendto string) error {
|
||||
FileHeader := make([]byte, 512)
|
||||
_, err = fd.Read(FileHeader)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return errors.Wrap(err, "failed to read header")
|
||||
}
|
||||
|
||||
FileContentType := http.DetectContentType(FileHeader)
|
||||
FileStat, _ := fd.Stat()
|
||||
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)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return errors.Wrap(err, "failed to seek")
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, fd)
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, fd)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
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)
|
||||
buf.WriteTo(w)
|
||||
|
||||
wwlog.Info("SEND: %15s: %s", sendto, filename)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -17,41 +16,43 @@ import (
|
||||
// TODO: https://github.com/pin/tftp
|
||||
|
||||
func RunServer() error {
|
||||
DaemonInitLogging()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
go func() {
|
||||
for range c {
|
||||
daemonLogf("Recieved SIGHUP, reloading...\n")
|
||||
wwlog.Warn("Recieved SIGHUP, reloading...")
|
||||
err := LoadNodeDB()
|
||||
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()
|
||||
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()
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: Could not load database: %s\n", err)
|
||||
wwlog.Error("Could not load database: %s", err)
|
||||
}
|
||||
|
||||
err = LoadNodeStatus()
|
||||
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("/kernel/", KernelSend)
|
||||
http.HandleFunc("/kmods/", KmodsSend)
|
||||
http.HandleFunc("/container/", ContainerSend)
|
||||
http.HandleFunc("/overlay-system/", SystemOverlaySend)
|
||||
http.HandleFunc("/overlay-runtime/", RuntimeOverlaySend)
|
||||
http.HandleFunc("/provision/", ProvisionSend)
|
||||
http.HandleFunc("/ipxe/", ProvisionSend)
|
||||
http.HandleFunc("/kernel/", ProvisionSend)
|
||||
http.HandleFunc("/kmods/", ProvisionSend)
|
||||
http.HandleFunc("/container/", ProvisionSend)
|
||||
http.HandleFunc("/overlay-system/", ProvisionSend)
|
||||
http.HandleFunc("/overlay-runtime/", ProvisionSend)
|
||||
http.HandleFunc("/status", StatusSend)
|
||||
|
||||
conf, err := warewulfconf.New()
|
||||
@@ -60,7 +61,7 @@ func RunServer() error {
|
||||
}
|
||||
|
||||
daemonPort := conf.Warewulf.Port
|
||||
daemonLogf("Starting HTTPD REST service on port %d\n", daemonPort)
|
||||
wwlog.Info("Starting HTTPD REST service on port %d", daemonPort)
|
||||
|
||||
err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,86 +3,287 @@ package wwlog
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
"runtime"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
CRITICAL = 0
|
||||
SECCRITICAL = 1
|
||||
ERROR = 2
|
||||
SECERROR = 3
|
||||
WARN = 4
|
||||
SECWARN = 5
|
||||
INFO = 6
|
||||
SECINFO = 7
|
||||
VERBOSE = 8
|
||||
SECVERBOSE = 9
|
||||
DEBUG = 10
|
||||
SECCRITICAL = 51
|
||||
CRITICAL = 50
|
||||
SECERROR = 41
|
||||
ERROR = 40
|
||||
SECWARN = 31
|
||||
WARN = 30
|
||||
SECINFO = 21
|
||||
INFO = 20
|
||||
SECVERBOSE = 16
|
||||
VERBOSE = 15
|
||||
SECDEBUG = 11
|
||||
DEBUG = 10
|
||||
NOTSET = 0
|
||||
)
|
||||
|
||||
var (
|
||||
logLevel = INFO
|
||||
)
|
||||
type LogRecord struct {
|
||||
Level int
|
||||
Err error
|
||||
Msg string
|
||||
Args []interface{}
|
||||
Pc uintptr
|
||||
File string
|
||||
Line int
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
/*
|
||||
Format a log message from a record
|
||||
rec.level >= logLevel
|
||||
*/
|
||||
type LogFormatter func(logLevel int, rec *LogRecord) string
|
||||
|
||||
/*
|
||||
Get string level name for level number
|
||||
*/
|
||||
func LevelName(level int) string {
|
||||
if level >= SECCRITICAL {
|
||||
return "SECCRITICAL"
|
||||
}
|
||||
if level >= CRITICAL {
|
||||
return "CRITICAL"
|
||||
}
|
||||
if level >= SECERROR {
|
||||
return "SECERROR"
|
||||
}
|
||||
if level >= ERROR {
|
||||
return "ERROR"
|
||||
}
|
||||
if level >= SECWARN {
|
||||
return "SECWARN"
|
||||
}
|
||||
if level >= WARN {
|
||||
return "WARN"
|
||||
}
|
||||
if level >= SECINFO {
|
||||
return "SECINFO"
|
||||
}
|
||||
if level >= INFO {
|
||||
return "INFO"
|
||||
}
|
||||
if level >= SECVERBOSE {
|
||||
return "SECVERBOSE"
|
||||
}
|
||||
if level >= VERBOSE {
|
||||
return "VERBOSE"
|
||||
}
|
||||
if level >= SECDEBUG {
|
||||
return "SECDEBUG"
|
||||
}
|
||||
if level >= DEBUG {
|
||||
return "DEBUG"
|
||||
}
|
||||
|
||||
return "NOTSET"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%-7s: %s", LevelName(rec.Level), message)
|
||||
}
|
||||
|
||||
var logLevel = INFO
|
||||
var logOut io.Writer = os.Stdout
|
||||
var logErr io.Writer = os.Stderr
|
||||
var logFormatter LogFormatter = DefaultFormatter
|
||||
|
||||
func EnabledForLevel(level int) bool {
|
||||
return level >= logLevel
|
||||
}
|
||||
|
||||
/*
|
||||
Set the central log level. Uneven values are security related
|
||||
lof entries.
|
||||
*/
|
||||
func SetLevel(level int) {
|
||||
func SetLogLevel(level int) {
|
||||
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 {
|
||||
switch level {
|
||||
case DEBUG:
|
||||
return "[DEBUG] : "
|
||||
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 SetLogWriters(out io.Writer, err io.Writer) {
|
||||
logOut = out
|
||||
logErr = err
|
||||
Debug("Set log writers")
|
||||
}
|
||||
|
||||
func printlog(level int, message string) {
|
||||
if level == INFO && logLevel <= INFO {
|
||||
fmt.Print(message)
|
||||
} else if level <= logLevel {
|
||||
if level < INFO {
|
||||
fmt.Fprintf(os.Stderr, prefixGen(level)+message)
|
||||
func GetLogWriters() (io.Writer, io.Writer) {
|
||||
return logOut, logErr
|
||||
}
|
||||
|
||||
/*
|
||||
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 recordLog(level int, err error, message string, a ...interface{}) {
|
||||
|
||||
if EnabledForLevel(level) {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
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.Fprintf(logErr, message)
|
||||
} else {
|
||||
fmt.Printf(prefixGen(level) + message)
|
||||
fmt.Fprintf(logOut, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Println(level int, message string) {
|
||||
printlog(level, message)
|
||||
recordLog(level, nil, message)
|
||||
}
|
||||
|
||||
func Printf(level int, message string, a ...interface{}) {
|
||||
printlog(level, fmt.Sprintf(message, a...))
|
||||
recordLog(level, nil, message, a...)
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Named log level functions
|
||||
*/
|
||||
func Log(level int, message string, a ...interface{}) {
|
||||
recordLog(level, nil, message, a...)
|
||||
}
|
||||
|
||||
func LogExc(level int, err error, message string, a ...interface{}) {
|
||||
recordLog(level, err, message, a...)
|
||||
}
|
||||
|
||||
func Debug(message string, a ...interface{}) {
|
||||
recordLog(DEBUG, nil, message, a...)
|
||||
}
|
||||
|
||||
func DebugExc(err error, message string, a ...interface{}) {
|
||||
recordLog(DEBUG, err, message, a...)
|
||||
}
|
||||
|
||||
func SecDebug(message string, a ...interface{}) {
|
||||
recordLog(SECDEBUG, nil, message, a...)
|
||||
}
|
||||
|
||||
func Verbose(message string, a ...interface{}) {
|
||||
recordLog(VERBOSE, nil, message, a...)
|
||||
}
|
||||
|
||||
func VerboseExc(err error, message string, a ...interface{}) {
|
||||
recordLog(VERBOSE, err, message, a...)
|
||||
}
|
||||
|
||||
func SecVerbose(message string, a ...interface{}) {
|
||||
recordLog(SECVERBOSE, nil, message, a...)
|
||||
}
|
||||
|
||||
func Info(message string, a ...interface{}) {
|
||||
recordLog(INFO, nil, message, a...)
|
||||
}
|
||||
|
||||
func InfoExc(err error, message string, a ...interface{}) {
|
||||
recordLog(INFO, err, message, a...)
|
||||
}
|
||||
|
||||
func SecInfo(message string, a ...interface{}) {
|
||||
recordLog(SECINFO, nil, message, a...)
|
||||
}
|
||||
|
||||
func Warn(message string, a ...interface{}) {
|
||||
recordLog(WARN, nil, message, a...)
|
||||
}
|
||||
|
||||
func WarnExc(err error, message string, a ...interface{}) {
|
||||
recordLog(WARN, err, message, a...)
|
||||
}
|
||||
|
||||
func SecWarn(message string, a ...interface{}) {
|
||||
recordLog(SECWARN, nil, message, a...)
|
||||
}
|
||||
|
||||
func Error(message string, a ...interface{}) {
|
||||
recordLog(ERROR, nil, message, a...)
|
||||
}
|
||||
|
||||
func ErrorExc(err error, message string, a ...interface{}) {
|
||||
recordLog(ERROR, err, message, a...)
|
||||
}
|
||||
|
||||
func SecError(message string, a ...interface{}) {
|
||||
recordLog(SECERROR, nil, message, a...)
|
||||
}
|
||||
|
||||
func Critical(message string, a ...interface{}) {
|
||||
recordLog(CRITICAL, nil, message, a...)
|
||||
}
|
||||
|
||||
func CriticalExc(err error, message string, a ...interface{}) {
|
||||
recordLog(CRITICAL, err, message, a...)
|
||||
}
|
||||
|
||||
func SecCritical(message string, a ...interface{}) {
|
||||
recordLog(SECCRITICAL, nil, message, a...)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{{if .Dhcp.Enabled -}}
|
||||
# This file is autogenerated by warewulf
|
||||
# This file is autogenerated by warewulf
|
||||
# Host: {{.BuildHost}}
|
||||
# Time: {{.BuildTime}}
|
||||
# Source: {{.BuildSource}}
|
||||
|
||||
{{if ne .Dhcp.Interface "" -}}
|
||||
INTERFACESv4="{{$.Dhcp.Interface}}";
|
||||
{{- end}}
|
||||
|
||||
allow booting;
|
||||
allow bootp;
|
||||
ddns-update-style interim;
|
||||
@@ -14,6 +19,16 @@ option space ipxe;
|
||||
option ipxe.no-pxedhcp code 176 = unsigned integer 8;
|
||||
option ipxe.no-pxedhcp 1;
|
||||
|
||||
# https://ipxe.org/cfg/syslogs
|
||||
{{if ne .Dhcp.Syslog "" -}}
|
||||
option log-servers {{$.Dhcp.Syslog}};
|
||||
{{end -}}
|
||||
{{if ne .Dhcp.Syslogs "" -}}
|
||||
option ipxe-encap-opts code 175 = encapsulate ipxe;
|
||||
option ipxe.syslogs code 85 = string;
|
||||
option ipxe.syslogs "{{$.Dhcp.Syslogs}}";
|
||||
{{end -}}
|
||||
|
||||
option architecture-type code 93 = unsigned integer 16;
|
||||
|
||||
if exists user-class and option user-class = "iPXE" {
|
||||
@@ -35,20 +50,35 @@ if exists user-class and option user-class = "iPXE" {
|
||||
}
|
||||
}
|
||||
|
||||
host {{$.Id}} {
|
||||
{{- if ne .Dhcp.Hwaddr ""}}
|
||||
hardware ethernet {{$.Dhcp.Hwaddr}};
|
||||
{{- end}}
|
||||
fixed-address {{$.Ipaddr}};
|
||||
option host-name "{{$.Id}}";
|
||||
}
|
||||
|
||||
{{if eq .Dhcp.Template "static" -}}
|
||||
subnet {{$.Network}} netmask {{$.Netmask}} {
|
||||
next-server {{$.Ipaddr}};
|
||||
}
|
||||
{{range $nodes := .AllNodes}}
|
||||
{{- range $netdevs := $nodes.NetDevs}}
|
||||
{{- if $netdevs.Default.Get}}
|
||||
host {{$nodes.Id.Get}} {
|
||||
hardware ethernet {{$netdevs.Hwaddr.Get}};
|
||||
fixed-address {{$netdevs.Ipaddr.Get}};
|
||||
{{- range $netname, $netdevs := $nodes.NetDevs}}
|
||||
host {{$nodes.Id.Get}}-{{if $netdevs.Device.Defined}}{{$netdevs.Device.Get}}{{else}}{{$netname}}{{end}} {
|
||||
{{- if $netdevs.Hwaddr.Defined}}
|
||||
hardware ethernet {{$netdevs.Hwaddr.Get}};
|
||||
{{- end}}
|
||||
{{- if $netdevs.Ipaddr.Defined}}
|
||||
fixed-address {{$netdevs.Ipaddr.Get}};
|
||||
{{- end}}
|
||||
{{- if $netdevs.Default.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 -}}
|
||||
|
||||
{{else -}}
|
||||
subnet {{$.Network}} netmask {{$.Netmask}} {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
{{ 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}}
|
||||
# Time: {{.BuildTime}}
|
||||
# Source: {{.BuildSource}}
|
||||
|
||||
# Warewulf Server
|
||||
{{$.Ipaddr}} {{$.BuildHost}}
|
||||
{{range $node := $.AllNodes}} {{/* for each node */}}
|
||||
# Entry for {{$node.Id.Get}}
|
||||
{{- 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.Default.GetB}} {{/* emit the node name as hostname if this is the default */}}
|
||||
{{$netdev.Ipaddr.Get}} {{$node.Id.Get}} {{$node.Id.Get}}-{{$devname}} {{$node.Id.Get}}-{{$netdev.Device.Get}}
|
||||
{{- else}}
|
||||
{{$netdev.Ipaddr.Get}} {{$node.Id.Get}}-{{$devname}} {{$node.Id.Get}}-{{$netdev.Device.Get}}
|
||||
{{- end}} {{/* end if default */}}
|
||||
{{- /* emit the node name as hostname if this is the default */}}
|
||||
{{$netdev.Ipaddr.Get}}
|
||||
{{- if $netdev.Default.GetB}} {{$node.Id.Get}}
|
||||
{{else}} {{$node.Id.Get}}-{{if $netdev.Device.Defined}}{{$netdev.Device.Get}}{{else}}{{$devname}}{{end}}
|
||||
{{- end}}
|
||||
{{- end}} {{/* end if ip */}}
|
||||
{{- end}} {{/* end for each network device */}}
|
||||
{{end}} {{/* end for each node */}}
|
||||
{{- end}} {{/* end for each node */}}
|
||||
|
||||
Reference in New Issue
Block a user