diff options
-rw-r--r-- | ChangeLog.d/feature/ZBXNEXT-6596 | 1 | ||||
-rw-r--r-- | src/go/pkg/zbxlib/checks_linux.go | 2 | ||||
-rw-r--r-- | src/go/plugins/proc/proc_linux.go | 110 | ||||
-rw-r--r-- | src/go/plugins/proc/procfs_linux.go | 24 | ||||
-rw-r--r-- | src/go/plugins/zabbix/sync/sync_nix.go | 1 |
5 files changed, 132 insertions, 6 deletions
diff --git a/ChangeLog.d/feature/ZBXNEXT-6596 b/ChangeLog.d/feature/ZBXNEXT-6596 new file mode 100644 index 00000000000..34bca246711 --- /dev/null +++ b/ChangeLog.d/feature/ZBXNEXT-6596 @@ -0,0 +1 @@ +...G...... [ZBXNEXT-6596] added native linux proc.num support plugin to Zabbix agent 2 (esneiders) diff --git a/src/go/pkg/zbxlib/checks_linux.go b/src/go/pkg/zbxlib/checks_linux.go index 99cefa30cc2..206ad9542d8 100644 --- a/src/go/pkg/zbxlib/checks_linux.go +++ b/src/go/pkg/zbxlib/checks_linux.go @@ -74,8 +74,6 @@ func resolveMetric(key string) (cfunc unsafe.Pointer) { cfunc = unsafe.Pointer(C.NET_DNS) case "net.dns.record": cfunc = unsafe.Pointer(C.NET_DNS_RECORD) - case "proc.num": - cfunc = unsafe.Pointer(C.PROC_NUM) case "system.boottime": cfunc = unsafe.Pointer(C.SYSTEM_BOOTTIME) case "net.tcp.listen": diff --git a/src/go/plugins/proc/proc_linux.go b/src/go/plugins/proc/proc_linux.go index 5b58d120719..20af05f34f8 100644 --- a/src/go/plugins/proc/proc_linux.go +++ b/src/go/plugins/proc/proc_linux.go @@ -121,6 +121,7 @@ type procQuery struct { name string user string cmdline string + state string } const ( @@ -128,6 +129,7 @@ const ( procInfoName procInfoUser procInfoCmdline + procInfoState ) type procInfo struct { @@ -136,6 +138,7 @@ type procInfo struct { userid int64 cmdline string arg0 string + state string } type cpuUtil struct { @@ -169,6 +172,7 @@ func newCpuUtilQuery(q *procQuery, pattern *regexp.Regexp) (query *cpuUtilQuery, return } } + query.cmdlinePattern = pattern return } @@ -181,13 +185,13 @@ func (p *Plugin) prepareQueries() (queries []*cpuUtilQuery, flags int) { p.mutex.Lock() for q, stats := range p.queries { if now.Sub(stats.accessed) > maxInactivityPeriod { - p.Debugf("removed unused CPU utilisation query %+v", q) + p.Debugf("removed unused CPU utilization query %+v", q) delete(p.queries, q) continue } var query *cpuUtilQuery if query, stats.err = newCpuUtilQuery(&q, stats.cmdlinePattern); stats.err != nil { - p.Debugf("cannot create CPU utilisation query %+v: %s", q, stats.err) + p.Debugf("cannot create CPU utilization query %+v: %s", q, stats.err) continue } queries = append(queries, query) @@ -411,9 +415,45 @@ func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) } return } +func (p *PluginExport) prepareQuery(q *procQuery) (query *cpuUtilQuery, flags int, err error) { + regxp, err := regexp.Compile(q.cmdline) + if err != nil { + return nil, 0, fmt.Errorf("cannot compile regex for %s: %s", q.cmdline, err.Error()) + } + + if query, err = newCpuUtilQuery(q, regxp); err != nil { + return nil, 0, fmt.Errorf("cannot create CPU utilization query %+v: %s", q, err.Error()) + } + + if q.name != "" { + flags |= procInfoName | procInfoCmdline + } + if q.user != "" { + flags |= procInfoUser + } + if q.cmdline != "" { + flags |= procInfoCmdline + } + if q.state != "" { + flags |= procInfoState + } + + return +} // Export - func (p *PluginExport) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) { + switch key { + case "proc.mem": + return p.exportProcMem(params) + case "proc.num": + return p.exportProcNum(params) + } + + return nil, plugin.UnsupportedMetricError +} + +func (p *PluginExport) exportProcMem(params []string) (result interface{}, err error) { var name, mode, cmdline, memtype string var usr *user.User @@ -592,6 +632,67 @@ func (p *PluginExport) Export(key string, params []string, ctx plugin.ContextPro return memSize, nil } +func (p *PluginExport) exportProcNum(params []string) (interface{}, error) { + var name, userName, state, cmdline string + switch len(params) { + case 4: + cmdline = params[3] + fallthrough + case 3: + switch params[2] { + case "all", "": + case "disk": + state = "D" + case "run": + state = "R" + case "sleep": + state = "S" + case "trace": + state = "T" + case "zomb": + state = "Z" + default: + return nil, errors.New("Invalid third parameter.") + } + fallthrough + case 2: + userName = params[1] + fallthrough + case 1: + name = params[0] + case 0: + default: + return nil, errors.New("Too many parameters.") + } + + var count int + + query, flags, err := p.prepareQuery(&procQuery{name, userName, cmdline, state}) + if err != nil { + p.Debugf("Failed to prepare query: %s", err.Error()) + return count, nil + } + + procs, err := getProcesses(flags) + if err != nil { + return nil, fmt.Errorf("Failed to get local processes: %s", err.Error()) + } + + for _, proc := range procs { + if !query.match(proc) { + continue + } + + if state != proc.state && state != "" { + continue + } + + count++ + } + + return count, nil +} + func getMax(a, b float64) float64 { if a > b { return a @@ -675,5 +776,8 @@ func (p *PluginExport) validFile(proc *procInfo, name string, uid int64, cmdRgx func init() { plugin.RegisterMetrics(&impl, "Proc", "proc.cpu.util", "Process CPU utilization percentage.") - plugin.RegisterMetrics(&implExport, "ProcExporter", "proc.mem", "Process memory utilization values.") + plugin.RegisterMetrics(&implExport, "ProcExporter", + "proc.mem", "Process memory utilization values.", + "proc.num", "The number of processes.", + ) } diff --git a/src/go/plugins/proc/procfs_linux.go b/src/go/plugins/proc/procfs_linux.go index cd9427647a4..e6e16379336 100644 --- a/src/go/plugins/proc/procfs_linux.go +++ b/src/go/plugins/proc/procfs_linux.go @@ -27,6 +27,7 @@ import ( "io" "os" "strconv" + "strings" "syscall" "zabbix.com/pkg/procfs" @@ -61,6 +62,22 @@ func getProcessName(pid string) (name string, err error) { return string(data[left+1 : right]), nil } +func getProcessState(pid string) (name string, err error) { + var data []byte + if data, err = read2k("/proc/" + pid + "/status"); err != nil { + return + } + + s := strings.Split(string(data), "\n") + for _, tmp := range s { + if strings.HasPrefix(tmp, "State:") && len(tmp) > 7 { + return string(tmp[7:8]), nil + } + } + + return "", fmt.Errorf("cannot find process state /proc/%s/status", pid) +} + func getProcessUserID(pid string) (userid int64, err error) { var fi os.FileInfo if fi, err = os.Stat("/proc/" + pid); err != nil { @@ -167,6 +184,13 @@ func getProcesses(flags int) (processes []*procInfo, err error) { continue } } + if flags&procInfoState != 0 { + if info.state, tmperr = getProcessState(entries[0].Name()); tmperr != nil { + impl.Debugf("cannot get process %s state: %s", entries[0].Name(), tmperr) + continue + } + } + processes = append(processes, info) } diff --git a/src/go/plugins/zabbix/sync/sync_nix.go b/src/go/plugins/zabbix/sync/sync_nix.go index b743d2e050c..f22ef75fa78 100644 --- a/src/go/plugins/zabbix/sync/sync_nix.go +++ b/src/go/plugins/zabbix/sync/sync_nix.go @@ -25,7 +25,6 @@ func getMetrics() []string { return []string{ "net.dns", "Checks if DNS service is up.", "net.dns.record", "Performs DNS query.", - "proc.num", "The number of processes.", "system.hw.chassis", "Chassis information.", "system.hw.devices", "Listing of PCI or USB devices.", "vfs.dir.count", "Directory entry count.", |