This is a work in progress, some of which will be reverted like overlay handling.

This commit is contained in:
Gregory Kurtzer
2020-11-25 23:58:38 -08:00
parent 56f38fa0e3
commit a4d2e3a0c8
25 changed files with 789 additions and 674 deletions

View File

@@ -1,15 +1,36 @@
profiles:
default:
vnfs: docker://warewulf/centos-8
kernel version: 3.10.0-1127.el7.x86_64
kernel args: crashkernel=no quiet
runtime overlay files:
- path: /etc/passwd
source: /path/to/source/etc/ssh/sshd.conf-node2
mode: 0644
- path: /etc/
mode: 0755
directory: true
- path: /etc/sshd.conf
source: /path/to/source/etc/ssh/sshd.conf-node2
mode: 0644
- path: /etc/link
source: /path/to/source/
mode: 0644
link: true
test:
runtime overlay files:
- path: /this/is/a/test/file
source: /path/to/source/etc/ssh/sshd.conf-node2
mode: 0644
nodegroups:
default:
comment: This is the default group
vnfs: docker://warewulf/centos-8
runtime overlay: default
domain suffix: default
kernel version: ""
kernel args: crashkernel=no quiet
nodes:
n0000:
hostname: n0000
kernel args: crashkernel=no
profiles: [default, test]
netdevs:
eth0:
hwaddr: 00:0c:29:23:8b:48
@@ -25,6 +46,7 @@ nodegroups:
n0001:
hostname: n0001
ipmi ipaddr: x.x.x.x
kernel version: BOOO
netdevs:
eth0:
hwaddr: aa:bb:cc:dd:ee:b0

View File

@@ -0,0 +1,13 @@
package main
import (
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/overlay"
)
func main() {
nodeDB, _ := node.New()
nodes, _ := nodeDB.FindAllNodes()
// wwlog.SetLevel(wwlog.DEBUG)
overlay.OverlayBuild(nodes, "runtime")
}

View File

@@ -49,10 +49,10 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) {
if node.HostName.Defined() == true {
conf := config.New()
log.Printf("IPXE: %15s: %s\n", node.Fqdn, req.URL.Path)
log.Printf("IPXE: %15s: %s\n", node.Fqdn.String(), req.URL.Path)
// TODO: Fix template path to use config package
ipxeTemplate := fmt.Sprintf("/etc/warewulf/ipxe/%s.ipxe", node.Ipxe)
ipxeTemplate := fmt.Sprintf("/etc/warewulf/ipxe/%s.ipxe", node.Ipxe.String())
tmpl, err := template.ParseFiles(ipxeTemplate)
if err != nil {
@@ -76,7 +76,7 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) {
return
}
log.Printf("SEND: %15s: %s\n", node.Fqdn, ipxeTemplate)
log.Printf("SEND: %15s: %s\n", node.Fqdn.String(), ipxeTemplate)
} else {
log.Printf("ERROR: iPXE request from unknown Node (hwaddr=%s)\n", url[2])

View File

@@ -23,12 +23,12 @@ func KernelSend(w http.ResponseWriter, req *http.Request) {
if err != nil {
log.Printf("ERROR: %s\n", err)
} else {
log.Printf("SEND: %15s: %s\n", node.Fqdn, fileName)
log.Printf("SEND: %15s: %s\n", node.Fqdn.String(), fileName)
}
} else {
w.WriteHeader(503)
log.Printf("ERROR: No 'kernel version' set for node %s\n", node.Fqdn)
log.Printf("ERROR: No 'kernel version' set for node %s\n", node.Fqdn.String())
}
return

View File

@@ -15,6 +15,7 @@ func KmodsSend(w http.ResponseWriter, req *http.Request) {
log.Println(err)
return
}
if node.KernelVersion.Defined() == true {
fileName := config.KmodsImage(node.KernelVersion.String())
@@ -22,12 +23,12 @@ func KmodsSend(w http.ResponseWriter, req *http.Request) {
if err != nil {
log.Printf("ERROR: %s\n", err)
} else {
log.Printf("SEND: %15s: %s\n", node.Fqdn, fileName)
log.Printf("SEND: %15s: %s\n", node.Fqdn.String(), fileName)
}
} else {
w.WriteHeader(503)
log.Printf("ERROR: No 'kernel version' set for node %s\n", node.Fqdn)
log.Printf("ERROR: No 'kernel version' set for node %s\n", node.Fqdn.String())
}
return

View File

@@ -52,7 +52,7 @@ func RuntimeOverlaySend(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(404)
return
} else {
log.Printf("REQ: %15s: %s\n", node.Fqdn, req.URL.Path)
log.Printf("REQ: %15s: %s\n", node.Fqdn.String(), req.URL.Path)
}
if node.RuntimeOverlay.Defined() == true {
@@ -62,11 +62,11 @@ func RuntimeOverlaySend(w http.ResponseWriter, req *http.Request) {
if err != nil {
log.Printf("ERROR: %s\n", err)
} else {
log.Printf("SEND: %15s: %s\n", node.Fqdn, fileName)
log.Printf("SEND: %15s: %s\n", node.Fqdn.String(), fileName)
}
} else {
w.WriteHeader(503)
log.Printf("ERROR: No 'runtime system-overlay' set for node %s\n", node.Fqdn)
log.Printf("ERROR: No 'runtime system-overlay' set for node %s\n", node.Fqdn.String())
}
return

View File

@@ -23,11 +23,11 @@ func SystemOverlaySend(w http.ResponseWriter, req *http.Request) {
if err != nil {
log.Printf("ERROR: %s\n", err)
} else {
log.Printf("SEND: %15s: %s\n", node.Fqdn, fileName)
log.Printf("SEND: %15s: %s\n", node.Fqdn.String(), fileName)
}
} else {
w.WriteHeader(503)
log.Printf("ERROR: No 'system system-overlay' set for node %s\n", node.Fqdn)
log.Printf("ERROR: No 'system system-overlay' set for node %s\n", node.Fqdn.String())
}
return

View File

@@ -31,7 +31,7 @@ func getSanity(req *http.Request) (node.NodeInfo, error) {
log.Printf("UNKNOWN: %15s: %s\n", hwaddr, req.URL.Path)
return ret, errors.New("Unknown node HW address: " + hwaddr)
} else {
log.Printf("REQ: %15s: %s\n", ret.Fqdn, req.URL.Path)
log.Printf("REQ: %15s: %s\n", ret.Fqdn.String(), req.URL.Path)
}
return ret, nil

View File

@@ -1,14 +1,13 @@
package response
import (
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"log"
"net/http"
)
func VnfsSend(w http.ResponseWriter, req *http.Request) {
config := config.New()
// config := config.New()
node, err := getSanity(req)
if err != nil {
@@ -18,17 +17,23 @@ func VnfsSend(w http.ResponseWriter, req *http.Request) {
}
if node.Vnfs.Defined() == true {
v := vnfs.New(node.Vnfs.String())
err := sendFile(w, config.VnfsImage(v.NameClean()), node.Fqdn.String())
v, err := vnfs.Load(node.Vnfs.String())
if err != nil {
log.Printf("ERROR: %s\n", err)
w.WriteHeader(503)
log.Printf("ERROR: Could not load VNFS: %s\n", node.Fqdn.String())
return
}
err = sendFile(w, v.Image, node.Fqdn.String())
if err != nil {
log.Printf("ERROR1: %s\n", err)
} else {
log.Printf("SEND: %15s: %s\n", node.Fqdn, config.VnfsImage(v.NameClean()))
log.Printf("SEND: %15s: %s\n", node.Fqdn.String(), v.Image)
}
} else {
w.WriteHeader(503)
log.Printf("ERROR: No VNFS set for node %s\n", node.Fqdn)
log.Printf("ERROR: No VNFS set for node %s\n", node.Fqdn.String())
}
return

View File

@@ -2,11 +2,52 @@ package list
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/spf13/cobra"
"io/ioutil"
"os"
"path"
"strings"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
fmt.Printf("This command is coming soon...\n")
config := config.New()
nconfig, _ := node.New()
nodes, _ := nconfig.FindAllNodes()
nodemap := make(map[string]int)
for _, n := range nodes {
nodemap[n.KernelVersion.String()] ++
}
images, _ := ioutil.ReadDir(config.KernelParentDir())
fmt.Printf("%-38s %-16s %-16s %s\n", "KERNEL VERSION", "KERNEL SIZE(k)", "KMODS SIZE(k)", "NODES")
fmt.Println(strings.Repeat("=", 80))
for _, file := range images {
if util.IsDir(path.Join(config.KernelParentDir(), file.Name())) {
var kernel_size int64
var kmods_size int64
if util.IsFile( config.KernelImage(file.Name())) {
s, _ := os.Stat(config.KernelImage(file.Name()))
kernel_size = s.Size() / 1024
}
if util.IsFile( config.KmodsImage(file.Name())) {
s, _ := os.Stat(config.KmodsImage(file.Name()))
kmods_size = s.Size() / 1024
}
if nodemap[file.Name()] > 0 {
fmt.Printf("%-38s %-16d %-16d %d\n", file.Name(), kernel_size, kmods_size, nodemap[file.Name()])
} else {
fmt.Printf("%-38s %-16d %-16d %d\n", file.Name(), kernel_size, kmods_size, 0)
}
}
}
return nil
}

View File

@@ -67,11 +67,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
} else if ShowLong == true {
fmt.Printf("%-22s %-12s %-26s %-30s %-12s\n", "NODE NAME", "GROUP NAME", "KERNEL VERSION", "VNFS IMAGE", "R-OVERLAY")
fmt.Println(strings.Repeat("=", 100))
fmt.Printf("%-22s %-12s %-26s %-35s %s\n", "NODE NAME", "GROUP NAME", "KERNEL VERSION", "VNFS IMAGE", "OVERLAYS (S/R)")
fmt.Println(strings.Repeat("=", 120))
for _, node := range nodes {
fmt.Printf("%-22s %-12s %-26s %-30s %-12s\n", node.Fqdn.String(), node.GroupName.Fprint(), node.KernelVersion.Fprint(), node.Vnfs.Fprint(), node.RuntimeOverlay.Fprint())
fmt.Printf("%-22s %-12s %-26s %-35s %s\n", node.Fqdn.String(), node.GroupName.Fprint(), node.KernelVersion.Fprint(), node.Vnfs.Fprint(), node.SystemOverlay.Fprint() +"/"+ node.RuntimeOverlay.Fprint())
}
} else {

View File

@@ -45,7 +45,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
wwlog.Printf(wwlog.INFO, "Updating System Overlays...\n")
err := overlay.SystemBuild(updateNodes, true)
if err != nil {
wwlog.Printf(wwlog.WARN, "Some system overlays failed to be generated\n")
wwlog.Printf(wwlog.WARN, "Some system overlays failed to be generated: %s\n", err)
}
}

View File

@@ -3,25 +3,62 @@ package list
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"github.com/spf13/cobra"
"io/ioutil"
"os"
"path"
"strings"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
config := config.New()
nconfig, _ := node.New()
nodes, _ := nconfig.FindAllNodes()
nodemap := make(map[string]int)
files, err := ioutil.ReadDir(config.VnfsImageParentDir())
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
for _, n := range nodes {
nodemap[n.Vnfs.String()] ++
}
for _, f := range files {
fmt.Println(f.Name())
}
images, _ := ioutil.ReadDir(config.VnfsImageParentDir())
fmt.Printf("VNFS LIST: work in progress: %s\n", config.VnfsImageParentDir())
fmt.Printf("%-38s %-16s %s\n", "VNFS Name", "VNFS SIZE(k)", "NODES")
fmt.Println(strings.Repeat("=", 80))
for _, file := range images {
v, err := vnfs.Load(file.Name())
if err == nil {
var vnfs_size int64
if util.IsFile( config.VnfsImage(file.Name())) {
s, _ := os.Stat(config.VnfsImage(file.Name()))
vnfs_size = s.Size() / 1024
}
if nodemap[v.Source] > 0 {
fmt.Printf("%-38s %-16d %d\n", v.Source, vnfs_size, nodemap[v.Source])
} else {
fmt.Printf("%-38s %-16d %d\n", v.Source, vnfs_size, 0)
}
}
continue
if util.IsDir(path.Join(config.VnfsImageParentDir(), file.Name())) {
var vnfs_size int64
if util.IsFile( config.VnfsImage(file.Name())) {
s, _ := os.Stat(config.VnfsImage(file.Name()))
vnfs_size = s.Size() / 1024
}
if nodemap[file.Name()] > 0 {
fmt.Printf("%-38s %-16d %d\n", file.Name(), vnfs_size, nodemap[file.Name()])
} else {
fmt.Printf("%-38s %-16d %d\n", file.Name(), vnfs_size, 0)
}
}
}
return nil
}

View File

@@ -92,6 +92,10 @@ func (self *Config) VnfsChrootParentDir() string {
return fmt.Sprintf("%s/chroot/", self.LocalStateDir)
}
func (self *Config) KernelParentDir() string {
return fmt.Sprintf("%s/provision/kernel/", self.LocalStateDir)
}
func (self *Config) SystemOverlaySource(overlayName string) string {
if overlayName == "" {
wwlog.Printf(wwlog.ERROR, "System overlay name is not defined\n")
@@ -132,7 +136,7 @@ func (self *Config) KernelImage(kernelVersion string) string {
return ""
}
return fmt.Sprintf("%s/provision/kernel/vmlinuz-%s", self.LocalStateDir, kernelVersion)
return path.Join(self.KernelParentDir(), kernelVersion, "vmlinuz")
}
func (self *Config) KmodsImage(kernelVersion string) string {
@@ -146,7 +150,7 @@ func (self *Config) KmodsImage(kernelVersion string) string {
return ""
}
return fmt.Sprintf("%s/provision/kernel/kmods-%s.img", self.LocalStateDir, kernelVersion)
return path.Join(self.KernelParentDir(), kernelVersion, "kmods.img")
}
func (self *Config) SystemOverlayImage(nodeName string) string {
@@ -177,32 +181,34 @@ func (self *Config) RuntimeOverlayImage(nodeName string) string {
return fmt.Sprintf("%s/provision/overlays/runtime/%s.img", self.LocalStateDir, nodeName)
}
func (self *Config) VnfsImageDir(uri string) string {
if uri == "" {
wwlog.Printf(wwlog.ERROR, "VNFS URI is not defined\n")
return ""
}
func (self *Config) VnfsImage(vnfsNameClean string) string {
if vnfsNameClean == "" {
if util.TaintCheck(uri, "^[a-zA-Z0-9-._:]+$") == false {
wwlog.Printf(wwlog.ERROR, "VNFS name contains illegal characters: %s\n", uri)
return ""
}
return path.Join(self.VnfsImageParentDir(), uri)
}
func (self *Config) VnfsImage(uri string) string {
return path.Join(self.VnfsImageDir(uri), "image")
}
func (self *Config) VnfsChroot(uri string) string {
if uri == "" {
wwlog.Printf(wwlog.ERROR, "VNFS name is not defined\n")
return ""
}
if util.TaintCheck(vnfsNameClean, "^[a-zA-Z0-9-._:]+$") == false {
wwlog.Printf(wwlog.ERROR, "Runtime overlay name contains illegal characters: %s\n", vnfsNameClean)
if util.TaintCheck(uri, "^[a-zA-Z0-9-._:]+$") == false {
wwlog.Printf(wwlog.ERROR, "VNFS name contains illegal characters: %s\n", uri)
return ""
}
return path.Join(self.VnfsImageParentDir(), vnfsNameClean)
return path.Join(self.VnfsChrootParentDir(), uri)
}
func (self *Config) VnfsChroot(vnfsNameClean string) string {
if vnfsNameClean == "" {
wwlog.Printf(wwlog.ERROR, "VNFS name is not defined\n")
return ""
}
if util.TaintCheck(vnfsNameClean, "^[a-zA-Z0-9-._:]+$") == false {
wwlog.Printf(wwlog.ERROR, "Runtime overlay name contains illegal characters: %s\n", vnfsNameClean)
return ""
}
return path.Join(self.VnfsChrootParentDir(), vnfsNameClean)
}

View File

@@ -98,32 +98,11 @@ func (self *nodeYaml) SetGroupVal(groupID string, entry string, value string) er
wwlog.Printf(wwlog.VERBOSE, "Setting group %s to: %s = '%s'\n", groupID, entry, value )
switch strings.ToUpper(entry) {
case "VNFS":
util.ValidateOrDie("VNFS", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].Vnfs = value
case "KERNEL":
util.ValidateOrDie("Kernel Version", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].KernelVersion = value
case "DOMAINSUFFIX":
util.ValidateOrDie("Domain", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].DomainSuffix = value
case "IPXE":
util.ValidateOrDie("iPXE Template", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].Ipxe = value
case "SYSTEMOVERLAY":
util.ValidateOrDie("System Overlay", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].SystemOverlay = value
case "RUNTIMEOVERLAY":
util.ValidateOrDie("Runtime Overlay", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].RuntimeOverlay = value
case "IPMIUSERNAME":
util.ValidateOrDie("IPMI Username", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].IpmiUserName = value
case "IPMIPASSWORD":
util.ValidateOrDie("IPMI Password", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].IpmiPassword = value
}
} else {
return errors.New("Group does not exist: " +groupID)
}
@@ -152,12 +131,6 @@ func (self *nodeYaml) SetNodeVal(groupID string, nodeID string, entry string, va
case "IPXE":
util.ValidateOrDie("iPXE Template", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].Nodes[nodeID].Ipxe = value
case "SYSTEMOVERLAY":
util.ValidateOrDie("System Overlay", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].Nodes[nodeID].SystemOverlay = value
case "RUNTIMEOVERLAY":
util.ValidateOrDie("Runtime Overlay", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].Nodes[nodeID].RuntimeOverlay = value
case "HOSTNAME":
util.ValidateOrDie("Hostname", entry, "^[a-zA-Z0-9-._]*$")
self.NodeGroups[groupID].Nodes[nodeID].Hostname = value
@@ -192,7 +165,7 @@ func (self *nodeYaml) SetNodeNet(groupID string, nodeID string, netDev string, e
if _, ok := self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev]; ok {
wwlog.Printf(wwlog.VERBOSE, "Editing existing node NetDev entry for node: %s/%s\n", groupID, nodeID)
} else {
var nd netDevs
var nd NetDevs
self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev] = &nd
}
} else {

View File

@@ -2,8 +2,9 @@ package node
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/errors"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"gopkg.in/yaml.v2"
"io/ioutil"
@@ -11,7 +12,6 @@ import (
)
const ConfigFile = "/etc/warewulf/nodes.conf"
const LocalStateDir = "/var/warewulf"
func init() {
//TODO: Check to make sure nodes.conf is found
@@ -19,45 +19,65 @@ func init() {
}
type nodeYaml struct {
NodeGroups map[string]*nodeGroup //`yaml:"nodegroups"`
Profiles map[string]*profileConfig
NodeGroups map[string]*nodeGroup
Hwaddrs map[string]NodeInfo
}
type profileConfig struct {
Vnfs string `yaml:"vnfs"`
Ipxe string `yaml:"ipxe template,omitempty"`
KernelVersion string `yaml:"kernel version"`
KernelArgs string `yaml:"kernel args"`
IpmiUserName string `yaml:"ipmi username,omitempty"`
IpmiPassword string `yaml:"ipmi password,omitempty"`
DomainSuffix string `yaml:"domain suffix,omitempty"`
RuntimeOverlay []OverlayEntry `yaml:"runtime overlay files,omitempty"`
SystemOverlay []OverlayEntry `yaml:"system overlay files,omitempty"`
}
type OverlayEntry struct {
Path string `yaml:"path,omitempty"`
File bool `yaml:"file,omitempty"`
Dir bool `yaml:"dir,omitempty"`
Link bool `yaml:"link,omitempty"`
Template bool `yaml:"template,omitempty"`
Mode int32 `yaml:"mode,omitempty"`
Owner string `yaml:"owner,omitempty"`
Group string `yaml:"group,omitempty"`
Source string `yaml:"source,omitempty"`
Sources []string `yaml:"sources,omitempty"`
}
type nodeGroup struct {
Comment string
Vnfs string `yaml:"vnfs"`
Ipxe string `yaml:"ipxe template,omitempty"`
SystemOverlay string `yaml:"system overlay,omitempty""`
RuntimeOverlay string `yaml:"runtime overlay""`
DomainSuffix string `yaml:"domain suffix"`
KernelVersion string `yaml:"kernel version"`
KernelArgs string `yaml:"kernel args"`
IpmiUserName string `yaml:"ipmi username,omitempty"`
IpmiPassword string `yaml:"ipmi password,omitempty"`
Nodes map[string]*nodeEntry
Comment string
DomainSuffix string `yaml:"domain suffix"`
Profiles []string `yaml:"profiles"`
Nodes map[string]*nodeEntry
}
type nodeEntry struct {
Hostname string `yaml:"hostname,omitempty"`
Vnfs string `yaml:"vnfs,omitempty"`
Ipxe string `yaml:"ipxe template,omitempty"`
SystemOverlay string `yaml:"system overlay,omitempty"`
RuntimeOverlay string `yaml:"runtime overlay,omitempty"`
DomainSuffix string `yaml:"domain suffix,omitempty"`
KernelVersion string `yaml:"kernel version,omitempty"`
KernelArgs string `yaml:"kernel args,omitempty"`
IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"`
IpmiUserName string `yaml:"ipmi username,omitempty"`
IpmiPassword string `yaml:"ipmi password,omitempty"`
NetDevs map[string]*netDevs
Hostname string `yaml:"hostname,omitempty"`
Vnfs string `yaml:"vnfs"`
Ipxe string `yaml:"ipxe template,omitempty"`
KernelVersion string `yaml:"kernel version"`
KernelArgs string `yaml:"kernel args"`
IpmiUserName string `yaml:"ipmi username,omitempty"`
IpmiPassword string `yaml:"ipmi password,omitempty"`
DomainSuffix string `yaml:"domain suffix,omitempty"`
IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"`
Profiles []string `yaml:"profiles"`
RuntimeOverlay []OverlayEntry `yaml:"system overlay files,omitempty"`
SystemOverlay []OverlayEntry `yaml:"runtime overlay files,omitempty"`
NetDevs map[string]*NetDevs
}
type netDevs struct {
Type string `yaml:"type,omitempty"`
Hwaddr string
Ipaddr string
Netmask string
Gateway string `yaml:"gateway,omitempty"`
type NetDevs struct {
Type string `yaml:"type,omitempty"`
Hwaddr string
Ipaddr string
Netmask string
Gateway string `yaml:"gateway,omitempty"`
}
type EntryInfo struct {
@@ -67,39 +87,43 @@ type EntryInfo struct {
}
type NodeInfo struct {
Id EntryInfo
Gid EntryInfo
Uid EntryInfo
GroupName EntryInfo
HostName EntryInfo
DomainName EntryInfo
Fqdn EntryInfo
Vnfs EntryInfo
Ipxe EntryInfo
SystemOverlay EntryInfo
RuntimeOverlay EntryInfo
KernelVersion EntryInfo
KernelArgs EntryInfo
IpmiIpaddr EntryInfo
IpmiUserName EntryInfo
IpmiPassword EntryInfo
NetDevs map[string]*netDevs
Id string
Gid string
Uid string
GroupName string
HostName string
DomainName string
Fqdn string
Vnfs string
VnfsRoot string
Ipxe string
KernelVersion string
KernelArgs string
IpmiIpaddr string
IpmiUserName string
IpmiPassword string
Profiles []string
RuntimeOverlay []OverlayEntry
SystemOverlay []OverlayEntry
NetDevs map[string]*NetDevs
}
type GroupInfo struct {
Id EntryInfo
GroupName EntryInfo
DomainName EntryInfo
Vnfs EntryInfo
Ipxe EntryInfo
SystemOverlay EntryInfo
RuntimeOverlay EntryInfo
KernelVersion EntryInfo
KernelArgs EntryInfo
IpmiUserName EntryInfo
IpmiPassword EntryInfo
Id string
GroupName string
DomainName string
Vnfs string
Ipxe string
SystemOverlay string
RuntimeOverlay string
KernelVersion string
KernelArgs string
IpmiUserName string
IpmiPassword string
}
func (i *EntryInfo) Default (value string) {
if value == "" {
return
@@ -174,17 +198,9 @@ func (self *nodeYaml) FindAllGroups() ([]GroupInfo, error) {
for groupname, group := range self.NodeGroups {
var g GroupInfo
g.Id.Set(groupname)
g.GroupName.Set(groupname)
g.RuntimeOverlay.Set(group.RuntimeOverlay)
g.SystemOverlay.Set(group.SystemOverlay)
g.Ipxe.Set(group.Ipxe)
g.KernelVersion.Set(group.KernelVersion)
g.KernelArgs.Set(group.KernelArgs)
g.Vnfs.Set(group.Vnfs)
g.IpmiUserName.Set(group.IpmiUserName)
g.IpmiPassword.Set(group.IpmiPassword)
g.DomainName.Set(group.DomainSuffix)
g.Id = groupname
g.GroupName = groupname
g.DomainName = group.DomainSuffix
// TODO: Validate or die on all inputs
@@ -201,55 +217,93 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
for nodename, node := range group.Nodes {
var n NodeInfo
n.Id.Set(nodename)
n.Gid.Set(groupname)
n.GroupName.Set(groupname)
n.HostName.Set(node.Hostname)
n.IpmiIpaddr.Set(node.IpmiIpaddr)
n.Id = nodename
n.Gid = groupname
n.GroupName = groupname
n.HostName = node.Hostname
n.Profiles = node.Profiles
n.IpmiIpaddr = node.IpmiIpaddr
n.Vnfs.Set(group.Vnfs)
n.SystemOverlay.Set(group.SystemOverlay)
n.RuntimeOverlay.Set(group.RuntimeOverlay)
n.KernelVersion.Set(group.KernelVersion)
n.KernelArgs.Set(group.KernelArgs)
n.DomainName.Set(group.DomainSuffix)
n.Ipxe.Set(group.Ipxe)
n.IpmiUserName.Set(group.IpmiUserName)
n.IpmiPassword.Set(group.IpmiPassword)
if len(n.Profiles) == 0 {
n.Profiles = append(n.Profiles, "default")
}
n.Vnfs.Override(node.Vnfs)
n.SystemOverlay.Override(node.SystemOverlay)
n.RuntimeOverlay.Override(node.RuntimeOverlay)
n.KernelVersion.Override(node.KernelVersion)
n.KernelArgs.Override(node.KernelArgs)
n.DomainName.Override(node.DomainSuffix)
n.Ipxe.Override(node.Ipxe)
n.IpmiUserName.Override(node.IpmiUserName)
n.IpmiPassword.Override(node.IpmiPassword)
n.RuntimeOverlay.Default("default")
n.SystemOverlay.Default("default")
n.Ipxe.Default("default")
if n.DomainName.Defined() == true {
if group.DomainSuffix != "" {
n.Fqdn.Set(node.Hostname + "." + group.DomainSuffix)
} else if node.DomainSuffix != "" {
n.Fqdn.Set(node.Hostname + "." + node.DomainSuffix)
for _, p := range n.Profiles {
if _, ok := self.Profiles[p]; ok {
} else {
n.Fqdn.Set(node.Hostname)
continue
}
if self.Profiles[p].Vnfs != "" {
n.Vnfs = self.Profiles[p].Vnfs
}
if self.Profiles[p].KernelVersion != "" {
n.KernelVersion = self.Profiles[p].KernelVersion
}
if self.Profiles[p].KernelArgs != "" {
n.KernelArgs = self.Profiles[p].KernelArgs
}
if self.Profiles[p].Ipxe != "" {
n.Ipxe = self.Profiles[p].Ipxe
}
if self.Profiles[p].IpmiUserName != "" {
n.IpmiUserName = self.Profiles[p].IpmiUserName
}
if self.Profiles[p].IpmiPassword != "" {
n.IpmiPassword = self.Profiles[p].IpmiPassword
}
if self.Profiles[p].DomainSuffix != "" {
n.DomainName = self.Profiles[p].DomainSuffix
}
for _, ro := range self.Profiles[p].RuntimeOverlay {
n.RuntimeOverlay = append(n.RuntimeOverlay, ro)
}
for _, so := range self.Profiles[p].SystemOverlay {
n.SystemOverlay = append(n.SystemOverlay, so)
}
}
n.NetDevs = node.NetDevs
if node.DomainSuffix != "" {
n.DomainName = node.DomainSuffix
} else if group.DomainSuffix != "" {
n.DomainName = group.DomainSuffix
}
if node.Vnfs != "" {
n.Vnfs = node.Vnfs
}
if node.KernelVersion != "" {
n.KernelVersion = node.KernelVersion
}
if node.KernelArgs != "" {
n.KernelArgs = node.KernelArgs
}
if node.Ipxe != "" {
n.Ipxe = node.Ipxe
}
if node.IpmiUserName != "" {
n.IpmiUserName = node.IpmiUserName
}
if node.IpmiPassword != "" {
n.IpmiPassword = node.IpmiPassword
}
util.ValidateOrDie(n.Fqdn.String() +":group name", n.GroupName.String(), "^[a-zA-Z0-9-._]*$")
util.ValidateOrDie(n.Fqdn.String() +":vnfs", n.Vnfs.String(), "^[a-zA-Z0-9-._:/]*$")
util.ValidateOrDie(n.Fqdn.String() +":system overlay", n.SystemOverlay.String(), "^[a-zA-Z0-9-._]*$")
util.ValidateOrDie(n.Fqdn.String() +":runtime overlay", n.RuntimeOverlay.String(), "^[a-zA-Z0-9-._]*$")
util.ValidateOrDie(n.Fqdn.String() +":domain suffix", n.DomainName.String(), "^[a-zA-Z0-9-._]*$")
util.ValidateOrDie(n.Fqdn.String() +":hostname", n.HostName.String(), "^[a-zA-Z0-9-_]*$")
util.ValidateOrDie(n.Fqdn.String() +":kernel version", n.KernelVersion.String(), "^[a-zA-Z0-9-._]*$")
if n.Ipxe == "" {
n.Ipxe = "default"
}
if n.KernelArgs == "" {
n.KernelArgs = "crashkernel=no quiet"
}
config := config.New()
n.VnfsRoot = config.VnfsChroot(vnfs.CleanName(n.Vnfs))
if n.DomainName != "" {
n.Fqdn = node.Hostname + "." + n.DomainName
} else {
n.Fqdn = node.Hostname
}
n.NetDevs = node.NetDevs
ret = append(ret, n)
}
@@ -296,7 +350,7 @@ func (nodes *nodeYaml) SearchByName(search string) ([]NodeInfo, error) {
n, _ := nodes.FindAllNodes()
for _, node := range n {
b, _ := regexp.MatchString(search, node.Fqdn.String())
b, _ := regexp.MatchString(search, node.Fqdn)
if b == true {
ret = append(ret, node)
}
@@ -312,7 +366,7 @@ func (nodes *nodeYaml) SearchByNameList(searchList []string) ([]NodeInfo, error)
for _, search := range searchList {
for _, node := range n {
b, _ := regexp.MatchString(search, node.Fqdn.String())
b, _ := regexp.MatchString(search, node.Fqdn)
if b == true {
ret = append(ret, node)
}
@@ -320,4 +374,4 @@ func (nodes *nodeYaml) SearchByNameList(searchList []string) ([]NodeInfo, error)
}
return ret, nil
}
}

View File

@@ -0,0 +1,43 @@
package overlay
import (
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"io/ioutil"
"path"
"strings"
)
func templateFileInclude(path string) string {
wwlog.Printf(wwlog.DEBUG, "Including file into template: %s\n", path)
content, err := ioutil.ReadFile(path)
if err != nil {
wwlog.Printf(wwlog.WARN, "Could not include file into template: %s\n", err)
}
return strings.TrimSuffix(string(content), "\n")
}
func templateVnfsFileInclude(vnfsname string, filepath string) string {
wwlog.Printf(wwlog.DEBUG, "Including VNFS file into template: %s: %s\n", vnfsname, filepath)
v, err := vnfs.Load(vnfsname)
if err != nil {
return ""
}
vnfsDir := v.Chroot
if util.IsDir(vnfsDir) == false {
wwlog.Printf(wwlog.WARN, "Template requesting file from non-imported VNFS: %s (%s)\n", vnfsname, filepath)
return ""
}
wwlog.Printf(wwlog.DEBUG, "IncludeVnfs file from: %s/%s\n", vnfsDir, filepath)
content, err := ioutil.ReadFile(path.Join(vnfsDir, filepath))
if err != nil {
wwlog.Printf(wwlog.ERROR, "Template include: %s\n", err)
}
return strings.TrimSuffix(string(content), "\n")
}

View File

@@ -1,41 +1,214 @@
package overlay
import (
"bytes"
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"strings"
"text/template"
)
func templateFileInclude(path string) string {
wwlog.Printf(wwlog.DEBUG, "Including file into template: %s\n", path)
content, err := ioutil.ReadFile(path)
if err != nil {
wwlog.Printf(wwlog.WARN, "Could not include file into template: %s\n", err)
}
return strings.TrimSuffix(string(content), "\n")
}
func templateVnfsFileInclude(vnfsname string, filepath string) string {
wwlog.Printf(wwlog.DEBUG, "Including VNFS file into template: %s: %s\n", vnfsname, filepath)
func OverlayBuild(nodeList []node.NodeInfo, overlayType string) error {
config := config.New()
v := vnfs.New(vnfsname)
vnfsdir := config.VnfsChroot(v.NameClean())
for _, n := range nodeList {
wwlog.Printf(wwlog.DEBUG, "Processing overlay for node: %s\n", n.Fqdn)
if util.IsDir(config.VnfsImage(vnfs.CleanName(n.Vnfs))) == false {
wwlog.Printf(wwlog.WARN, "'%s', VNFS not built: %s\n", n.Fqdn, n.Vnfs)
// continue
}
var files []node.OverlayEntry
tmpDir, err := ioutil.TempDir(os.TempDir(), ".wwctl-runtime-overlay-")
if err != nil {
continue
}
if overlayType == "system" {
wwlog.Printf(wwlog.VERBOSE, "Generating system overlay for node: %s\n", n.Fqdn)
files = n.SystemOverlay
} else if overlayType == "runtime" {
wwlog.Printf(wwlog.VERBOSE, "Generating runtime overlay for node: %s\n", n.Fqdn)
files = n.RuntimeOverlay
} else {
wwlog.Printf(wwlog.ERROR, "overlayType requested is not supported: %s\n", overlayType)
os.Exit(1)
}
for _, file := range files {
fullPath := path.Join(tmpDir, file.Path)
mode := file.Mode
if mode == 0 {
mode = 0755
}
if file.Dir == true {
wwlog.Printf(wwlog.DEBUG, "Creating overlay directory: %s\n", file.Path)
err := os.MkdirAll(fullPath, os.FileMode(mode))
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create parent directory in overlay: %s\n", file.Path)
continue
}
} else if file.File == true {
wwlog.Printf(wwlog.DEBUG, "Creating overlay file: %s\n", file.Path)
err := os.MkdirAll(path.Dir(fullPath), 0755)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create parent directory for overlay file: %s\n", file.Dir)
continue
}
write, err := os.OpenFile(path.Join(tmpDir, file.Path), os.O_RDWR|os.O_CREATE, os.FileMode(mode))
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
continue
}
if file.Source != "" {
tmpl, _ := template.New("string").Parse(file.Source)
var tpl bytes.Buffer
_ = tmpl.Execute(&tpl, n)
string := tpl.String()
source, err := os.Open(string)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
source.Close()
continue
}
_, err = io.Copy(write, source)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
source.Close()
continue
}
source.Close()
}
for _, s := range file.Sources {
tmpl, _ := template.New("string").Parse(s)
var tpl bytes.Buffer
_ = tmpl.Execute(&tpl, n)
string := tpl.String()
source, err := os.Open(string)
if err != nil {
wwlog.Printf(wwlog.WARN, "%s\n", err)
source.Close()
continue
}
_, err = io.Copy(write, source)
if err != nil {
wwlog.Printf(wwlog.WARN, "%s\n", err)
source.Close()
continue
}
source.Close()
}
write.Close()
} else if file.Link == true {
wwlog.Printf(wwlog.DEBUG, "Creating overlay link: %s\n", file.Path)
if file.Source != "" {
err := os.MkdirAll(path.Dir(fullPath), 0755)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create parent directory for overlay file: %s\n", file.Path)
continue
}
err = os.Symlink(file.Source, fullPath)
if err != nil {
wwlog.Printf(wwlog.WARN, "Could not create overlay symlink: %s\n", err)
continue
}
} else {
wwlog.Printf(wwlog.WARN, "Could not create link, no source defined: %s\n", file.Path)
continue
}
} else if file.Template == true {
wwlog.Printf(wwlog.DEBUG, "Creating overlay file from template: %s\n", file.Path)
err := os.MkdirAll(path.Dir(fullPath), 0755)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create parent directory for overlay file: %s\n", file.Path)
continue
}
if file.Source != "" {
tmpl, err := template.New(path.Base(file.Source)).Funcs(template.FuncMap{
"Include": templateFileInclude,
"IncludeFromVnfs": templateVnfsFileInclude,
}).ParseFiles(file.Source)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
continue
}
w, err := os.OpenFile(fullPath, os.O_RDWR|os.O_CREATE, os.FileMode(mode))
if err != nil {
w.Close()
wwlog.Printf(wwlog.ERROR, "%s\n", err)
continue
}
err = tmpl.Execute(w, n)
if err != nil {
w.Close()
wwlog.Printf(wwlog.ERROR, "%s\n", err)
continue
}
w.Close()
}
} else {
wwlog.Printf(wwlog.ERROR, "Overlay file type error: %s\n", file.Path)
}
}
var overlayFile string
if overlayType == "system" {
overlayFile = config.SystemOverlayImage(n.Fqdn)
} else if overlayType == "runtime" {
overlayFile = config.RuntimeOverlayImage(n.Fqdn)
}
err = os.MkdirAll(path.Dir(overlayFile), 0755)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create parent directory for overlay file: %s\n", overlayFile)
continue
}
wwlog.Printf(wwlog.VERBOSE, "Finished generating overlay directory for: %s\n", n.Fqdn)
os.Chmod(tmpDir, 0755)
cmd := fmt.Sprintf("cd \"%s\"; find . | cpio --quiet -o -H newc -F \"%s\"", tmpDir, overlayFile)
wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd)
err = exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not generate runtime image overlay: %s\n", err)
os.RemoveAll(tmpDir)
continue
}
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", n.Fqdn)
wwlog.Printf(wwlog.DEBUG, "Removing temporary directory: %s\n", tmpDir)
os.RemoveAll(tmpDir)
if util.IsDir(vnfsdir) == false {
wwlog.Printf(wwlog.WARN, "Template requesting file from non-imported VNFS: %s (%s)\n", vnfsname, filepath)
return ""
}
wwlog.Printf(wwlog.DEBUG, "IncludeVnfs file from: %s/%s\n", vnfsdir, filepath)
content, err := ioutil.ReadFile(path.Join(vnfsdir, filepath))
if err != nil {
wwlog.Printf(wwlog.ERROR, "Template include: %s\n", err)
}
return strings.TrimSuffix(string(content), "\n")
return nil
}

View File

@@ -1,181 +0,0 @@
package overlay
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/errors"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
)
func FindAllRuntimeOverlays() ([]string, error) {
config := config.New()
var ret []string
wwlog.Printf(wwlog.DEBUG, "Looking for runtime overlays...")
files, err := ioutil.ReadDir(config.RuntimeOverlayDir())
if err != nil {
return ret, err
}
for _, file := range files {
wwlog.Printf(wwlog.DEBUG, "Evaluating runtime overlay: %s\n", file.Name())
if file.IsDir() == true {
ret = append(ret, file.Name())
}
}
return ret, nil
}
func RuntimeOverlayInit(name string) error {
config := config.New()
path := config.RuntimeOverlaySource(name)
if util.IsDir(path) == true {
return errors.New("Runtime overlay already exists: "+name)
}
err := os.MkdirAll(path, 0755)
return err
}
func RuntimeBuild(nodeList []node.NodeInfo, force bool) error {
config := config.New()
wwlog.SetIndent(4)
for _, node := range nodeList {
if node.RuntimeOverlay.String() != "" {
OverlayDir := config.RuntimeOverlaySource(node.RuntimeOverlay.String())
OverlayFile := config.RuntimeOverlayImage(node.Fqdn.String())
vnfs := vnfs.New(node.Vnfs.String())
vnfsDir := config.VnfsChroot(vnfs.NameClean())
wwlog.Printf(wwlog.VERBOSE, "Building Runtime Overlay for: %s\n", node.Fqdn)
tmpDir, err := ioutil.TempDir(os.TempDir(), ".wwctl-runtime-overlay-")
if err != nil {
return err
}
if util.IsDir(OverlayDir) == false {
wwlog.Printf(wwlog.WARN, "%-35s: Skipped (runtime overlay template not found)\n", node.Fqdn)
continue
}
if util.IsDir(vnfsDir) == false {
wwlog.Printf(wwlog.WARN, "%-35s: Skipped (VNFS not imported)\n", node.Fqdn)
continue
}
err = os.MkdirAll(path.Dir(OverlayFile), 0755)
if err != nil {
return err
}
if force == false {
wwlog.Printf(wwlog.DEBUG, "Checking if overlay is required\n")
}
if util.PathIsNewer(OverlayDir, OverlayFile) {
if force == false {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, overlay is current\n", node.Fqdn)
continue
}
}
wwlog.Printf(wwlog.DEBUG, "Changing directory to OverlayDir: %s\n", OverlayDir)
err = os.Chdir(OverlayDir)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not chdir() to OverlayDir: %s\n", OverlayDir)
continue
}
wwlog.Printf(wwlog.DEBUG, "Walking the file system: %s\n", OverlayDir)
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
wwlog.Printf(wwlog.DEBUG, "Found directory: %s\n", location)
err := os.MkdirAll(path.Join(tmpDir, location), info.Mode())
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
} else if filepath.Ext(location) == ".ww" {
wwlog.Printf(wwlog.DEBUG, "Found template file: %s\n", location)
destFile := strings.TrimSuffix(location, ".ww")
tmpl, err := template.New(path.Base(location)).Funcs(template.FuncMap{"Include": templateFileInclude, "IncludeFromVnfs": templateVnfsFileInclude}).ParseGlob(path.Join(OverlayDir, destFile + ".ww*"))
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
w, err := os.OpenFile(path.Join(tmpDir, destFile), os.O_RDWR|os.O_CREATE, info.Mode())
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
defer w.Close()
err = tmpl.Execute(w, node)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
} else if b, _ := regexp.MatchString(`\.ww[a-zA-Z0-9\-\._]*$`, location); b == true {
wwlog.Printf(wwlog.DEBUG, "Ignoring WW template file: %s\n", location)
} else {
wwlog.Printf(wwlog.DEBUG, "Found file: %s\n", location)
err := util.CopyFile(path.Join(OverlayDir, location), path.Join(tmpDir, location))
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
}
return nil
})
wwlog.Printf(wwlog.VERBOSE, "Finished generating overlay directory for: %s\n", node.Fqdn)
cmd := fmt.Sprintf("cd \"%s\"; find . | cpio --quiet -o -H newc -F \"%s\"", tmpDir, OverlayFile)
wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd)
err = exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not generate runtime image overlay: %s\n", err)
continue
}
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", node.Fqdn)
wwlog.Printf(wwlog.DEBUG, "Removing temporary directory: %s\n", tmpDir)
os.RemoveAll(tmpDir)
}
}
wwlog.SetIndent(0)
return nil
}

View File

@@ -1,181 +0,0 @@
package overlay
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/errors"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/vnfs"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
)
func FindAllSystemOverlays() ([]string, error) {
config := config.New()
var ret []string
wwlog.Printf(wwlog.DEBUG, "Looking for system overlays...")
files, err := ioutil.ReadDir(config.SystemOverlayDir())
if err != nil {
return ret, err
}
for _, file := range files {
wwlog.Printf(wwlog.DEBUG, "Evaluating system overlay: %s\n", file.Name())
if file.IsDir() == true {
ret = append(ret, file.Name())
}
}
return ret, nil
}
func SystemOverlayInit(name string) error {
config := config.New()
path := config.SystemOverlaySource(name)
if util.IsDir(path) == true {
return errors.New("Runtime overlay already exists: "+name)
}
err := os.MkdirAll(path, 0755)
return err
}
func SystemBuild(nodeList []node.NodeInfo, force bool) error {
config := config.New()
wwlog.SetIndent(4)
for _, node := range nodeList {
if node.SystemOverlay.String() != "" {
OverlayDir := config.SystemOverlaySource(node.SystemOverlay.String())
OverlayFile := config.SystemOverlayImage(node.Fqdn.String())
vnfs := vnfs.New(node.Vnfs.String())
vnfsDir := config.VnfsChroot(vnfs.NameClean())
wwlog.Printf(wwlog.VERBOSE, "Building System Overlay for: %s\n", node.Fqdn)
tmpDir, err := ioutil.TempDir(os.TempDir(), ".wwctl-system-overlay-")
if err != nil {
return err
}
if util.IsDir(OverlayDir) == false {
wwlog.Printf(wwlog.WARN, "%-35s: Skipped (system overlay template not found)\n", node.Fqdn)
continue
}
if util.IsDir(vnfsDir) == false {
wwlog.Printf(wwlog.WARN, "%-35s: Skipped (VNFS not imported)\n", node.Fqdn)
continue
}
err = os.MkdirAll(path.Dir(OverlayFile), 0755)
if err != nil {
return err
}
if force == false {
wwlog.Printf(wwlog.DEBUG, "Checking if overlay is required\n")
}
if util.PathIsNewer(OverlayDir, OverlayFile) {
if force == false {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, overlay is current\n", node.Fqdn)
continue
}
}
wwlog.Printf(wwlog.DEBUG, "Changing directory to OverlayDir: %s\n", OverlayDir)
err = os.Chdir(OverlayDir)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not chdir() to OverlayDir: %s\n", OverlayDir)
continue
}
wwlog.Printf(wwlog.DEBUG, "Walking the file system: %s\n", OverlayDir)
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
wwlog.Printf(wwlog.DEBUG, "Found directory: %s\n", location)
err := os.MkdirAll(path.Join(tmpDir, location), info.Mode())
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
} else if filepath.Ext(location) == ".ww" {
wwlog.Printf(wwlog.DEBUG, "Found template file: %s\n", location)
destFile := strings.TrimSuffix(location, ".ww")
tmpl, err := template.New(path.Base(location)).Funcs(template.FuncMap{"Include": templateFileInclude, "IncludeFromVnfs": templateVnfsFileInclude}).ParseGlob(path.Join(OverlayDir, destFile + ".ww*"))
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
w, err := os.OpenFile(path.Join(tmpDir, destFile), os.O_RDWR|os.O_CREATE, info.Mode())
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
defer w.Close()
err = tmpl.Execute(w, node)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
} else if b, _ := regexp.MatchString(`\.ww[a-zA-Z0-9\-\._]*$`, location); b == true {
wwlog.Printf(wwlog.DEBUG, "Ignoring WW template file: %s\n", location)
} else {
wwlog.Printf(wwlog.DEBUG, "Found file: %s\n", location)
err := util.CopyFile(path.Join(OverlayDir, location), path.Join(tmpDir, location))
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return err
}
}
return nil
})
wwlog.Printf(wwlog.VERBOSE, "Finished generating overlay directory for: %s\n", node.Fqdn)
cmd := fmt.Sprintf("cd \"%s\"; find . | cpio --quiet -o -H newc -F \"%s\"", tmpDir, OverlayFile)
wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd)
err = exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not generate system image overlay: %s\n", err)
continue
}
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", node.Fqdn)
wwlog.Printf(wwlog.DEBUG, "Removing temporary directory: %s\n", tmpDir)
os.RemoveAll(tmpDir)
}
}
wwlog.SetIndent(0)
return nil
}

View File

@@ -37,6 +37,8 @@ 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)
time1, err := DirModTime(source)
if err != nil {
wwlog.Printf(wwlog.DEBUG, "%s\n", err)

View File

@@ -2,63 +2,61 @@ package vnfs
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"os"
"path"
)
func BuildContainerdir(v VnfsObject, buildForce bool) {
config := config.New()
func BuildContainerdir(vnfs VnfsObject, buildForce bool) {
if _, err := os.Stat(v.Source()); err != nil {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping (bad path)\n", v.Name())
if _, err := os.Stat(vnfs.Source); err != nil {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping (bad path)\n", vnfs.Source)
return
}
wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n")
if util.PathIsNewer(v.Source(), config.VnfsImage(v.NameClean())) {
if util.PathIsNewer(vnfs.Source, vnfs.Image) {
if buildForce == false {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", v.Name())
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", vnfs.Name)
return
}
}
wwlog.Printf(wwlog.DEBUG, "Making the directory: %s\n", path.Dir(config.VnfsImage(v.NameClean())))
err := os.MkdirAll(path.Dir(config.VnfsImage(v.NameClean())), 0755)
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", vnfs.Image)
err := os.MkdirAll(path.Dir(vnfs.Image), 0755)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
wwlog.Printf(wwlog.DEBUG, "Making the directory: %s\n", path.Dir(config.VnfsChroot(v.NameClean())))
err = os.MkdirAll(path.Dir(config.VnfsChroot(v.NameClean())), 0755)
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", vnfs.Chroot)
err = os.MkdirAll(path.Dir(vnfs.Chroot), 0755)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", v.Source(), config.VnfsImage(v.NameClean()))
err = buildVnfs(v.Source(), config.VnfsImage(v.NameClean()))
wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", vnfs.Source, vnfs.Image)
err = buildVnfs(vnfs.Source, vnfs.Image)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
os.Exit(1)
}
// Setup links from OCI rootfs to chroot path
_ = os.Remove(config.VnfsChroot(v.NameClean()) + "-link")
err = os.Symlink(v.Source(), config.VnfsChroot(v.NameClean())+"-link")
_ = os.Remove(vnfs.Chroot + "-link")
err = os.Symlink(vnfs.Source, vnfs.Chroot+"-link")
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create symlink for Chroot: %s\n", err)
os.Exit(1)
}
err = os.Rename(config.VnfsChroot(v.NameClean())+"-link", config.VnfsChroot(v.NameClean()))
err = os.Rename(vnfs.Chroot + "-link", vnfs.Chroot)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not rename link: %s\n", err)
os.Exit(1)
}
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", v.Name())
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", vnfs.Name)
}

View File

@@ -11,8 +11,8 @@ import (
)
func BuildDocker(v VnfsObject, buildForce bool) {
wwlog.Printf(wwlog.VERBOSE, "Building OCI Container: %s\n", v.Source())
func BuildDocker(vnfs VnfsObject, buildForce bool) {
wwlog.Printf(wwlog.VERBOSE, "Building OCI Container: %s\n", vnfs.Source)
config := config.New()
OciCacheDir := config.LocalStateDir + "/oci"
@@ -25,7 +25,7 @@ func BuildDocker(v VnfsObject, buildForce bool) {
}
wwlog.Printf(wwlog.VERBOSE, "Downloading OCI container layers\n")
sourcePath, err := c.Pull(context.Background(), v.Source(), nil)
sourcePath, err := c.Pull(context.Background(), vnfs.Source, nil)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
os.Exit(1)
@@ -33,10 +33,10 @@ func BuildDocker(v VnfsObject, buildForce bool) {
hashDestination := VnfsHashDir + path.Base(sourcePath)
name, err := os.Readlink(config.VnfsImage(v.NameClean()))
name, err := os.Readlink(vnfs.Image)
if err == nil {
if name == hashDestination && buildForce == false {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", v.Name())
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", vnfs.Name)
return
}
}
@@ -46,12 +46,16 @@ func BuildDocker(v VnfsObject, buildForce bool) {
fmt.Printf("ERROR: %s\n", err)
return
}
err = os.MkdirAll(path.Dir(config.VnfsImage(v.NameClean())), 0755)
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", vnfs.Image)
err = os.MkdirAll(path.Dir(vnfs.Image), 0755)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
err = os.MkdirAll(path.Dir(config.VnfsChroot(v.NameClean())), 0755)
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", vnfs.Chroot)
err = os.MkdirAll(path.Dir(vnfs.Chroot), 0755)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
@@ -68,33 +72,33 @@ func BuildDocker(v VnfsObject, buildForce bool) {
wwlog.Printf(wwlog.VERBOSE, "Finalizing Build\n")
// Setup links from OCI image to provision path
_ = os.Remove(config.VnfsImage(v.NameClean()) + "-link")
err = os.Symlink(hashDestination, config.VnfsImage(v.NameClean())+"-link")
_ = os.Remove(vnfs.Image + "-link")
err = os.Symlink(hashDestination, vnfs.Image + "-link")
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create symlink for Image: %s\n", err)
os.Exit(1)
}
err = os.Rename(config.VnfsImage(v.NameClean())+"-link", config.VnfsImage(v.NameClean()))
err = os.Rename(vnfs.Image + "-link", vnfs.Image)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not rename link: %s\n", err)
os.Exit(1)
}
// Setup links from OCI rootfs to chroot path
_ = os.Remove(config.VnfsChroot(v.NameClean()) + "-link")
err = os.Symlink(sourcePath, config.VnfsChroot(v.NameClean())+"-link")
_ = os.Remove(vnfs.Chroot + "-link")
err = os.Symlink(sourcePath, vnfs.Chroot + "-link")
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create symlink for Chroot: %s\n", err)
os.Exit(1)
}
err = os.Rename(config.VnfsChroot(v.NameClean())+"-link", config.VnfsChroot(v.NameClean()))
err = os.Rename(vnfs.Chroot + "-link", vnfs.Chroot)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not rename link: %s\n", err)
os.Exit(1)
}
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", v.Name())
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", vnfs.Source)
return
}

View File

@@ -1,70 +1,175 @@
package vnfs
import (
"github.com/hpcng/warewulf/internal/pkg/config"
"github.com/hpcng/warewulf/internal/pkg/errors"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"path"
"strings"
)
type VnfsObject struct {
SourcePath string
Name string
Source string
Chroot string
Image string
Config string
}
func New(s string) VnfsObject {
func Load (name string) (VnfsObject, error) {
config := config.New()
var ret VnfsObject
pathFriendlyName := CleanName(name)
ret.SourcePath = s
configFile := path.Join(config.VnfsImageDir(pathFriendlyName), "config.yaml")
return ret
if util.IsFile(configFile) == false {
return ret, errors.New("No Configuration for VNFS name: " + configFile)
}
data, err := ioutil.ReadFile(configFile)
if err != nil {
return ret, errors.New("Error reading VNFS configuration file: " + name)
}
err = yaml.Unmarshal(data, &ret)
if err != nil {
return ret, err
}
return ret, nil
}
func (self *VnfsObject) Name() string {
if self.SourcePath == "" {
return ""
}
func CleanName(source string) string {
var tmp string
if strings.HasPrefix(self.SourcePath, "/") {
return path.Base(self.SourcePath)
}
return self.SourcePath
}
func (self *VnfsObject) NameClean() string {
if self.SourcePath == "" {
return ""
}
if strings.HasPrefix(self.SourcePath, "/") {
return path.Base(self.SourcePath)
}
uri := strings.Split(self.SourcePath, "://")
return strings.ReplaceAll(uri[0]+":"+uri[1], "/", "_")
}
func (self *VnfsObject) Source() string {
if self.SourcePath == "" {
return ""
}
return self.SourcePath
}
func Build(uri string, force bool) error {
v := New(uri)
wwlog.Printf(wwlog.VERBOSE, "Building VNFS: %s\n", uri)
if strings.HasPrefix(uri, "/") {
if strings.HasSuffix(uri, "tar.gz") {
//wwlog.Printf(wwlog.WARN, "Building VNFS from local tarball: %s\n", uri)
wwlog.Printf(wwlog.WARN, "Building VNFS from local tarball is not supported yet: %s\n", uri)
} else {
BuildContainerdir(v, force)
}
if strings.HasPrefix(source, "/") == true {
tmp = path.Base(source)
} else {
BuildDocker(v, force)
tmp = source
}
tmp = strings.ReplaceAll(tmp, "://", "-")
tmp = strings.ReplaceAll(tmp, "/", ".")
tmp = strings.ReplaceAll(tmp, ":", ".")
return tmp
}
func New(source string) (VnfsObject, error) {
var ret VnfsObject
config := config.New()
pathFriendlyName := CleanName(source)
if strings.HasPrefix(source, "/") == true {
ret.Source = source
ret.Name = pathFriendlyName
} else {
tmp := strings.ReplaceAll(source, "://", "-")
tmp = strings.ReplaceAll(tmp, "/", ".")
tmp = strings.ReplaceAll(tmp, ":", ".")
ret.Name = source
ret.Source = source
}
ret.Chroot = config.VnfsChroot(pathFriendlyName)
ret.Image = config.VnfsImage(pathFriendlyName)
ret.Config = path.Join(config.VnfsImageDir(pathFriendlyName), "config.yaml")
if util.IsFile(ret.Config) {
return Load(source)
}
return ret, nil
}
func (self *VnfsObject) SaveConfig() error {
out, err := yaml.Marshal(self)
if err != nil {
return err
}
file, err := os.OpenFile(self.Config, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
defer file.Close()
_, err = file.WriteString(string(out))
if err != nil {
return err
}
return nil
}
func Build(name string, force bool) error {
vnfs, err := New(name)
if err != nil {
return err
}
wwlog.Printf(wwlog.VERBOSE, "Building VNFS: %s\n", vnfs.Name)
if strings.HasPrefix(vnfs.Source, "/") {
if strings.HasSuffix(vnfs.Source, "tar.gz") {
//wwlog.Printf(wwlog.WARN, "Building VNFS from local tarball: %s\n", uri)
wwlog.Printf(wwlog.WARN, "Building VNFS from local tarball is not supported yet: %s\n", vnfs.Name)
} else {
BuildContainerdir(vnfs, force)
}
} else {
BuildDocker(vnfs, force)
}
err = vnfs.SaveConfig()
if err != nil {
return err
}
return nil
}
func (self *VnfsObject) Nameold() string {
if self.Source == "" {
return ""
}
if strings.HasPrefix(self.Source, "/") {
return path.Base(self.Source)
}
return self.Source
}
func NameClean1(SourcePath string) string {
if SourcePath == "" {
return ""
}
if strings.HasPrefix(SourcePath, "/") {
return path.Base(SourcePath)
}
uri := strings.Split(SourcePath, "://")
return strings.ReplaceAll(uri[0]+":"+uri[1], "/", "_")
}

View File

@@ -1,8 +1,8 @@
DEVICE=eth0
NAME=eth0
IPADDR={{.NetDevs.eth0.Ipaddr}}
NETMASK={{.NetDevs.eth0.Netmask}}
GATEWAY={{.NetDevs.eth0.Gateway}}
HWADDR={{.NetDevs.eth0.Hwaddr}}
IPADDR={{.Netdev.eth0.Ipaddr}}
NETMASK={{.Netdev.eth0.Netmask}}
GATEWAY={{.Netdev.eth0.Gateway}}
HWADDR={{.Netdev.eth0.Hwaddr}}
BOOTPROTO=static
ONBOOT=yes