208 lines
4.4 KiB
Go
208 lines
4.4 KiB
Go
/* Copyright (C) 2022, M. Höß, hoess@gmx.net. MIT Licensed */
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"jobwatch/state"
|
|
"jobwatch/types"
|
|
)
|
|
|
|
// Jobs root-idfile
|
|
|
|
func evalOutput(job types.Job, output []string) (types.CMKState, error) {
|
|
var res types.CMKState = types.OK
|
|
var logLines []string
|
|
var rcs []*regexp.Regexp
|
|
|
|
for _, m := range job.LogMatches {
|
|
r, err := regexp.Compile(m.Regex)
|
|
if err != nil {
|
|
return 0, err
|
|
} else {
|
|
rcs = append(rcs, r)
|
|
}
|
|
}
|
|
|
|
// Determine state and wanted loglines
|
|
for _, v := range output {
|
|
for idx, m := range job.LogMatches {
|
|
if rcs[idx].Match([]byte(v)) {
|
|
if m.State > res {
|
|
res = m.State
|
|
}
|
|
|
|
if m.AltMsg != "" {
|
|
v = fmt.Sprintf(m.AltMsg, v)
|
|
}
|
|
|
|
var p = ""
|
|
switch s := m.State; s {
|
|
case types.OK:
|
|
p = "O"
|
|
case types.WARN:
|
|
p = "W"
|
|
case types.CRIT:
|
|
p = "C"
|
|
case types.UNKNOWN:
|
|
p = "U"
|
|
}
|
|
p += ": "
|
|
logLines = append(logLines, time.Now().Format("2006-01-2 15:04:05")+" "+p+v)
|
|
|
|
}
|
|
}
|
|
}
|
|
for _, oln := range logLines {
|
|
log.Printf("- Matched LogLine %v\n", oln)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func runJob(job types.Job) (state.State, error) {
|
|
|
|
st := state.StateFromJob(job)
|
|
st.LastExitCode = -1
|
|
st.LastState = int(types.UNKNOWN)
|
|
st.LastRun = time.Now().Format(time.RFC3339)
|
|
|
|
cmd := exec.Command(job.Cmd, append(job.Args, job.InstArgs...)...)
|
|
|
|
var stdoutBuf, stderrBuf, stdcombinedBuf bytes.Buffer
|
|
if job.HideOutput {
|
|
cmd.Stdout = io.MultiWriter(&stdoutBuf, &stdcombinedBuf)
|
|
cmd.Stderr = io.MultiWriter(&stderrBuf, &stdcombinedBuf)
|
|
} else {
|
|
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf, &stdcombinedBuf)
|
|
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf, &stdcombinedBuf)
|
|
}
|
|
|
|
var stcode = 0
|
|
if err := cmd.Run(); err != nil {
|
|
// cmd executed, but failed w/ non-zero
|
|
if exitError, ok := err.(*exec.ExitError); ok {
|
|
stcode = exitError.ProcessState.ExitCode()
|
|
} else {
|
|
// cmd not found etc
|
|
return st, err
|
|
}
|
|
if stcode == 0 { // Exec failed, but exitcode, be sure to get error!
|
|
stcode = int(types.UNKNOWN)
|
|
}
|
|
} else {
|
|
stcode = cmd.ProcessState.ExitCode()
|
|
}
|
|
var exitCode = stcode
|
|
log.Printf("- ExitCode of Command: %v", stcode)
|
|
|
|
for _, em := range job.ExitCodeMap {
|
|
// From == -1 -> Alle non-zero-states mappen
|
|
if (stcode == int(em.From)) || (em.From == -1 && stcode > 0) {
|
|
stcode = int(em.To)
|
|
}
|
|
}
|
|
if stcode > int(types.UNKNOWN) {
|
|
stcode = int(types.CRIT)
|
|
}
|
|
log.Printf("- State after ExitCodeMapping: %v", stcode)
|
|
|
|
outText := string(stdcombinedBuf.Bytes())
|
|
log_state, err := evalOutput(job, strings.Split(outText, "\n"))
|
|
if int(log_state) > stcode {
|
|
stcode = int(log_state)
|
|
}
|
|
|
|
log.Printf("- State after Output-evaluation: %v", stcode)
|
|
|
|
st.LastExitCode = exitCode
|
|
st.LastState = stcode
|
|
st.LastRun = time.Now().Format(time.RFC3339)
|
|
|
|
return st, err
|
|
}
|
|
|
|
func setup() (*types.Job, error) {
|
|
var jobdir string
|
|
var jobid string
|
|
var job_instance string
|
|
var debug bool
|
|
|
|
flag.StringVar(&jobdir, "jd", "", "JobDir. Default: $HOME/etc/jobwatch.d /etc/jobwatch.d'")
|
|
flag.StringVar(&jobid, "j", "", "JobId. reads $jobDir/$job.job.yml Default ''")
|
|
flag.StringVar(&job_instance, "i", "", "JobId Instance. Default ''")
|
|
flag.BoolVar(&debug, "d", false, "Debug")
|
|
flag.Parse()
|
|
|
|
if debug {
|
|
log.SetOutput(os.Stderr)
|
|
} else {
|
|
log.SetOutput(ioutil.Discard)
|
|
}
|
|
|
|
if jobdir == "" && jobid == "" && job_instance == "" {
|
|
// No params -> Show mode
|
|
return nil, nil
|
|
}
|
|
|
|
if !regexp.MustCompile("^[a-z0-9\\-]*$").MatchString(jobid) {
|
|
return nil, fmt.Errorf("-j: invalid chars")
|
|
}
|
|
|
|
if !regexp.MustCompile("^[a-z0-9\\-]*$").MatchString(job_instance) {
|
|
return nil, fmt.Errorf("-i: invalid chars")
|
|
}
|
|
|
|
log.Printf(". Raw args : %+v\n", flag.Args())
|
|
log.Printf(". Job onstance id : %+v\n", job_instance)
|
|
|
|
if job, err := types.LoadJob(jobdir, jobid); err == nil {
|
|
job.InstArgs = flag.Args()
|
|
job.InstId = job_instance
|
|
return job, nil
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
job, err := setup()
|
|
|
|
if job == nil && err == nil {
|
|
fmt.Println("<<<jobwatch:sep(0)>>>")
|
|
state.ShowAll()
|
|
fmt.Println("<<<>>>")
|
|
} else {
|
|
fmt.Fprintln(os.Stderr, "Jobwatch 0.1 (C) 2022 hoess@gmx.net")
|
|
|
|
var res state.State
|
|
if err == nil {
|
|
res, err = runJob(*job)
|
|
}
|
|
|
|
res.ReEval()
|
|
res.SaveState()
|
|
|
|
if err != nil {
|
|
fmt.Printf("! Error running job %+v\n", err)
|
|
os.Exit(int(types.UNKNOWN))
|
|
} else {
|
|
os.Exit(int(res.LastState))
|
|
}
|
|
|
|
state.WriteLog(*job)
|
|
}
|
|
|
|
}
|