diff --git a/Changes b/Changes index e67ec5e..201dd0b 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ This file documents the revision history for the SNClient+ agent. +next: + - add check_ping + 0.25 Fri Jun 14 14:34:25 CEST 2024 - update node exporter to 1.8.1 - migrate v3 to github.com/shirou/gopsutil/v4 diff --git a/packaging/snclient.ini b/packaging/snclient.ini index 2c1b0f7..39ad463 100644 --- a/packaging/snclient.ini +++ b/packaging/snclient.ini @@ -55,6 +55,9 @@ CheckSystemUnix = enabled ; CheckDisk - Controls wether check_drivesize is allowed or not. CheckDisk = enabled +; CheckNet - Controls wether check_ping is allowed or not. +CheckNet = disabled + ; CheckWMI - Controls wether check_wmi is allowed or not. CheckWMI = disabled diff --git a/pkg/snclient/check_ping.go b/pkg/snclient/check_ping.go new file mode 100644 index 0000000..de19650 --- /dev/null +++ b/pkg/snclient/check_ping.go @@ -0,0 +1,103 @@ +package snclient + +import ( + "context" + "fmt" + "runtime" +) + +func init() { + AvailableChecks["check_ping"] = CheckEntry{"check_ping", NewCheckPing} +} + +type CheckPing struct { + snc *Agent + hosts []string + timeout int64 + count int64 +} + +func NewCheckPing() CheckHandler { + return &CheckPing{ + hosts: []string{}, + timeout: 500, + count: 1, + } +} + +func (l *CheckPing) Build() *CheckData { + return &CheckData{ + name: "check_ping", + description: "Checks a remote host availability.", + implemented: ALL, + hasInventory: NoInventory, + result: &CheckResult{ + State: CheckExitOK, + }, + args: map[string]CheckArgument{ + "host": {value: &l.hosts, description: "The remote host to ping."}, + "hosts": {value: &l.hosts, description: "Alias for host."}, + "timeout": {value: &l.timeout, description: "Ping timeout in milliseconds. Default: 500ms"}, + "count": {value: &l.count, description: "Number of packets to send. Default: 1"}, + }, + defaultWarning: "time > 60 or loss > 5%", + defaultCritical: "time > 100 or loss > 10%", + okSyntax: "%(status): All %(count) hosts are ok", + detailSyntax: "${ip} Packet loss = ${loss}%, RTA = ${time}ms", + topSyntax: "${status}: ${ok_count}/${count} (${problem_list})", + emptyState: CheckExitUnknown, + emptySyntax: "No hosts found", + attributes: []CheckAttribute{ + {name: "host", description: "The host name or ip address (as given on command line)"}, + {name: "ip", description: "The resolved ip address"}, + {name: "loss", description: "Packet loss (in percent)"}, + {name: "recv", description: "Number of packets received from the host"}, + {name: "sent", description: "Number of packets sent to the host"}, + {name: "timeout", description: "Number of packets which timed out from the host"}, + {name: "time", description: "Round trip time in ms"}, + }, + exampleDefault: ` + check_ping host=omd.consol.de + OK - TODO: ... + `, + exampleArgs: `host=omd.consol.de timeout=100 count=2`, + } +} + +func (l *CheckPing) Check(ctx context.Context, snc *Agent, check *CheckData, _ []Argument) (*CheckResult, error) { + l.snc = snc + + enabled, _, _ := snc.config.Section("/modules").GetBool("CheckNet") + if !enabled { + return nil, fmt.Errorf("module CheckNet is not enabled in /modules section") + } + + if len(l.hosts) == 0 { + return nil, fmt.Errorf("must specify at least one host") + } + + switch runtime.GOOS { + case "linux": + for _, host := range l.hosts { + err := l.addPingLinux(ctx, check, host) + if err != nil { + return nil, err + } + } + default: + return nil, fmt.Errorf("os %s is not supported", runtime.GOOS) + } + + return check.Finalize() +} + +func (l *CheckPing) addPingLinux(ctx context.Context, check *CheckData, host string) error { + output, stderr, _, err := l.snc.execCommand(ctx, fmt.Sprintf("ping %s -c %d -W %d", host, l.count, l.timeout), DefaultCmdTimeout) + if err != nil { + return fmt.Errorf("ping failed: %s\n%s", err.Error(), stderr) + } + + log.Debugf("%s", output) + + return nil +} diff --git a/pkg/snclient/check_ping_test.go b/pkg/snclient/check_ping_test.go new file mode 100644 index 0000000..bbe7737 --- /dev/null +++ b/pkg/snclient/check_ping_test.go @@ -0,0 +1,26 @@ +package snclient + +import ( + "regexp" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCheckPing(t *testing.T) { + config := ` +[/modules] +CheckBuiltinPlugins = enabled +` + snc := StartTestAgent(t, config) + + res := snc.RunCheck("check_ping", []string{"host=127.0.0.1", "count=1", "timeout=5000"}) + assert.Equalf(t, CheckExitOK, res.State, "state ok") + assert.Regexpf(t, + regexp.MustCompile(`^OK - [\d.]+ seconds response time on port 45666`), + string(res.BuildPluginOutput()), + "output matches", + ) + + StopTestAgent(t, snc) +}