diff options
Diffstat (limited to '')
-rw-r--r-- | src/go/plugin/go.d/modules/monit/collect.go | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/monit/collect.go b/src/go/plugin/go.d/modules/monit/collect.go new file mode 100644 index 000000000..580aa6d99 --- /dev/null +++ b/src/go/plugin/go.d/modules/monit/collect.go @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package monit + +import ( + "encoding/xml" + "errors" + "fmt" + "io" + "net/http" + "net/url" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web" + + "golang.org/x/net/html/charset" +) + +var ( + urlPathStatus = "/_status" + urlQueryStatus = url.Values{"format": {"xml"}, "level": {"full"}}.Encode() +) + +func (m *Monit) collect() (map[string]int64, error) { + mx := make(map[string]int64) + + if err := m.collectStatus(mx); err != nil { + return nil, err + } + + return mx, nil +} + +func (m *Monit) collectStatus(mx map[string]int64) error { + status, err := m.fetchStatus() + if err != nil { + return err + } + + if status.Server == nil { + // not Monit + return errors.New("invalid Monit status response: missing server data") + } + + mx["uptime"] = status.Server.Uptime + + seen := make(map[string]bool) + + for _, svc := range status.Services { + seen[svc.id()] = true + + if _, ok := m.seenServices[svc.id()]; !ok { + m.seenServices[svc.id()] = svc + m.addServiceCheckCharts(svc, status.Server) + } + + px := fmt.Sprintf("service_check_type_%s_name_%s_status_", svc.svcType(), svc.Name) + + for _, v := range []string{"not_monitored", "ok", "initializing", "error"} { + mx[px+v] = 0 + if svc.status() == v { + mx[px+v] = 1 + } + } + } + + for id, svc := range m.seenServices { + if !seen[id] { + delete(m.seenServices, id) + m.removeServiceCharts(svc) + } + } + + return nil +} + +func (m *Monit) fetchStatus() (*monitStatus, error) { + req, err := web.NewHTTPRequestWithPath(m.Request, urlPathStatus) + if err != nil { + return nil, err + } + req.URL.RawQuery = urlQueryStatus + + var status monitStatus + if err := m.doOKDecode(req, &status); err != nil { + return nil, err + } + + return &status, nil +} + +func (m *Monit) doOKDecode(req *http.Request, in interface{}) error { + resp, err := m.httpClient.Do(req) + if err != nil { + return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err) + } + defer closeBody(resp) + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode) + } + + dec := xml.NewDecoder(resp.Body) + dec.CharsetReader = charset.NewReaderLabel + + if err := dec.Decode(in); err != nil { + return fmt.Errorf("error on decoding XML response from '%s': %v", req.URL, err) + } + + return nil +} + +func closeBody(resp *http.Response) { + if resp != nil && resp.Body != nil { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } +} |