diff --git a/perfdata/util.go b/perfdata/util.go index a070139..843c6d4 100644 --- a/perfdata/util.go +++ b/perfdata/util.go @@ -8,7 +8,7 @@ import ( ) // Lists all allowed characters inside a label, so we can replace any non-matching -var validInLabelRe = regexp.MustCompile(`[^a-zA-Z0-9 _\-+:/.]+`) +var validInLabelRe = regexp.MustCompile(`[^a-zA-Z0-9 _\-+:/.;]+`) // FormatNumeric returns a string representation of various possible numerics // diff --git a/result/overall.go b/result/overall.go index febcd25..86bd8e7 100644 --- a/result/overall.go +++ b/result/overall.go @@ -4,30 +4,54 @@ package result import ( "fmt" "github.com/NETWAYS/go-check" + "github.com/NETWAYS/go-check/perfdata" "strings" ) +// So, this is the idea: +// A check plugin has a single Overall (singleton) +// Each partial thing which is tested, gets it's own subcheck +// The results of these may be relevant to the overall status in the end +// or not, e.g. if a plugin trieds two different methods for something and +// one suffices, but one fails, the whole check might be OK and only the subcheck +// Warning or Critical. type Overall struct { OKs int Warnings int Criticals int Unknowns int Summary string - Outputs []string + Outputs []string // Deprecate this in a future version + partialResults []PartialResult } +type PartialResult struct { + State int + Output string + Perfdata perfdata.PerfdataList + partialResults []PartialResult +} + +func (s *PartialResult) String() string { + return fmt.Sprintf("[%s] %s|%s", check.StatusText(s.State), s.Output, s.Perfdata.String()) +} + +// Deprecate this in a future version func (o *Overall) AddOK(output string) { o.Add(check.OK, output) } +// Deprecate this in a future version func (o *Overall) AddWarning(output string) { o.Add(check.Warning, output) } +// Deprecate this in a future version func (o *Overall) AddCritical(output string) { o.Add(check.Critical, output) } +// Deprecate this in a future version func (o *Overall) AddUnknown(output string) { o.Add(check.Unknown, output) } @@ -47,6 +71,14 @@ func (o *Overall) Add(state int, output string) { o.Outputs = append(o.Outputs, fmt.Sprintf("[%s] %s", check.StatusText(state), output)) } +func (o *Overall) AddSubcheck(subcheck PartialResult) { + o.partialResults = append(o.partialResults, subcheck) +} + +func (o *PartialResult) AddSubcheck(subcheck PartialResult) { + o.partialResults = append(o.partialResults, subcheck) +} + func (o *Overall) GetStatus() int { if o.Criticals > 0 { return check.Critical @@ -79,9 +111,37 @@ func (o *Overall) GetSummary() string { o.Summary += fmt.Sprintf("ok=%d ", o.OKs) } - if o.Summary == "" { + if o.Summary == "" && len(o.partialResults) == 0 { o.Summary = "No status information" } else { + criticals := 0 + warnings := 0 + oks := 0 + unknowns := 0 + for _, sc := range o.partialResults { + if sc.State == check.Critical { + criticals++ + } else if sc.State == check.Warning { + warnings++ + } else if sc.State == check.Unknown { + unknowns++ + } else if sc.State == check.OK { + oks++ + } + } + + if criticals > 0 { + o.Summary += fmt.Sprintf("critical=%d ", criticals) + } + if unknowns > 0 { + o.Summary += fmt.Sprintf("unknowns=%d ", unknowns) + } + if warnings > 0 { + o.Summary += fmt.Sprintf("warning=%d ", warnings) + } + if oks > 0 { + o.Summary += fmt.Sprintf("ok=%d ", oks) + } o.Summary = "states: " + strings.TrimSpace(o.Summary) } } @@ -96,6 +156,26 @@ func (o *Overall) GetOutput() string { output += extra + "\n" } + if o.partialResults != nil { + for _, s := range o.partialResults { + output += s.getOutput(0) + } + } + return output } +func (s *PartialResult) getOutput(indent_level int) string { + var output string + + prefix := strings.Repeat(" ", indent_level) + output += prefix + "\\_ " + s.String() + "\n" + + if s.partialResults != nil { + for _, ss := range s.partialResults { + output += ss.getOutput(indent_level + 2) + } + } + + return output +} diff --git a/result/overall_test.go b/result/overall_test.go index dbde9c6..36f43a4 100644 --- a/result/overall_test.go +++ b/result/overall_test.go @@ -3,6 +3,7 @@ package result import ( "fmt" "github.com/NETWAYS/go-check" + "github.com/NETWAYS/go-check/perfdata" "github.com/stretchr/testify/assert" "testing" ) @@ -90,7 +91,7 @@ func ExampleOverall_Add() { overall.Add(check.Critical, "The other is critical") fmt.Println(overall) - // Output: {1 0 1 0 [[OK] One element is good [CRITICAL] The other is critical]} + // Output: {1 0 1 0 [[OK] One element is good [CRITICAL] The other is critical] []} } func ExampleOverall_GetOutput() { @@ -113,3 +114,93 @@ func ExampleOverall_GetStatus() { fmt.Println(overall.GetStatus()) // Output: 2 } + +func ExampleOverall_withSubchecks() { + var overall Overall + + example_perfdata := perfdata.Perfdata{Label: "pd_test", Value: 5, Uom: "s"} + pd_list := perfdata.PerfdataList{} + pd_list.Add(&example_perfdata) + + subcheck := PartialResult{ + State: check.OK, + Output: "Subcheck1 Test", + Perfdata: pd_list, + } + + overall.AddSubcheck(subcheck) + overall.AddOK("bla") + + fmt.Println(overall.GetOutput()) + // Output: + // states: ok=1 ok=1 + // [OK] bla + // \_ [OK] Subcheck1 Test|pd_test=5s +} + +//nolint +func ExampleOverall_withSubchecks2() { + var overall Overall + + example_perfdata := perfdata.Perfdata{Label: "pd_test", Value: 5, Uom: "s"} + example_perfdata2 := perfdata.Perfdata{ + Label: "pd_test2", + Value: 1099511627776, + Uom: "kB", + Warn: &check.Threshold{Inside: true, Lower: 3.14, Upper: 0x66666666666}, + Crit: &check.Threshold{Inside: false, Lower: 07777777777777, Upper: 0xFFFFFFFFFFFFFFFFFFFF}, + Max: uint64(18446744073709551615), + } + example_perfdata3 := perfdata.Perfdata{Label: "kl;jr2if;l2rkjasdf", Value: 5, Uom: "m"} + example_perfdata4 := perfdata.Perfdata{Label: "asdf", Value: uint64(18446744073709551615), Uom: "B"} + + pd_list := perfdata.PerfdataList{} + pd_list.Add(&example_perfdata) + pd_list.Add(&example_perfdata2) + + pd_list2 := perfdata.PerfdataList{} + pd_list2.Add(&example_perfdata3) + pd_list2.Add(&example_perfdata4) + + subcheck := PartialResult{ + State: check.OK, + Output: "Subcheck1 Test", + Perfdata: pd_list, + } + subcheck2 := PartialResult{ + State: check.Warning, + Output: "Subcheck2 Test", + Perfdata: pd_list2, + } + + overall.AddSubcheck(subcheck) + overall.AddSubcheck(subcheck2) + + fmt.Println(overall.GetOutput()) + + // states: warning=1 ok=1 + // \_ [OK] Subcheck1 Test|pd_test=5s pd_test2=1099511627776kB;@3.14:7036874417766;549755813887:1208925819614629174706176;;18446744073709551615 + // \_ [WARNING] Subcheck2 Test|kl;jr2if;l2rkjasdf=5m asdf=18446744073709551615B +} + +func ExampleOverall_withSubchecks3() { + var overall Overall + + subcheck2 := PartialResult{ + State: check.OK, + Output: "SubSubcheck", + } + subcheck := PartialResult{ + State: check.OK, + Output: "PartialResult", + } + subcheck.partialResults = append(subcheck.partialResults, subcheck2) + + overall.AddSubcheck(subcheck) + + fmt.Println(overall.GetOutput()) + + // states: ok=1 + // \_ [OK] PartialResult| + // \_ [OK] SubSubcheck| +}