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:
Carter Dodd
2022-05-07 01:36:18 -05:00
parent fea148903c
commit 379faad51d
30 changed files with 1214 additions and 794 deletions

View File

@@ -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...)
}