add go.d/monit (#18344)
Ilya Mashchenko committed
Aug 16, 2024 at 17:20 UTC
fa0a549fbeda71c82412d4dc198284220ccfa365
16 files changed
+1971
-1
src/collectors/python.d.plugin/python.d.conf
+1
-1
@@ -34,7 +34,6 @@ gc_interval: 300
34
example: no
35
go_expvar: no
36
# haproxy: yes
37
-# monit: yes
37
# openldap: yes
38
# oracledb: yes
39
# pandas: yes
@@ -67,6 +66,7 @@ litespeed: no # Removed (replaced with go.d/litespeed).
66
megacli: no # Removed (replaced with go.d/megacli).
67
memcached: no # Removed (replaced with go.d/memcached).
68
mongodb: no # Removed (replaced with go.d/mongodb).
69
+monit: no # Removed (replaced with go.d/monit).
70
mysql: no # Removed (replaced with go.d/mysql).
71
nginx: no # Removed (replaced with go.d/nginx).
72
nsd: no # Removed (replaced with go.d/nsd).
src/go/plugin/go.d/README.md
+1
@@ -101,6 +101,7 @@ see the appropriate collector readme.
101
| [megacli](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/megacli) | MegaCli Hardware Raid |
102
| [memcached](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/memcached) | Memcached |
103
| [mongoDB](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/mongodb) | MongoDB |
104
+| [monit](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/monit) | Monit |
105
| [mysql](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/mysql) | MySQL |
106
| [nginx](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/nginx) | NGINX |
107
| [nginxplus](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/nginxplus) | NGINX Plus |
src/go/plugin/go.d/config/go.d.conf
+1
@@ -65,6 +65,7 @@ modules:
65
# megacli: yes
66
# memcached: yes
67
# mongodb: yes
68
+# monit: yes
69
# mysql: yes
70
# nginx: yes
71
# nginxplus: yes
src/go/plugin/go.d/config/go.d/monit.conf
new
+6
@@ -0,0 +1,6 @@
1
+## All available configuration options, their descriptions and default values:
2
+## https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/monit#readme
3
+
4
+#jobs:
5
+# - name: local
6
+# url: http://127.0.0.1:2812
src/go/plugin/go.d/config/go.d/sd/net_listeners.conf
+9
@@ -80,6 +80,8 @@ classify:
80
expr: '{{ or (eq .Port "11211") (eq .Comm "memcached") }}'
81
- tags: "mongodb"
82
expr: '{{ or (eq .Port "27017") (eq .Comm "mongod") }}'
83
+ - tags: "monit"
84
+ expr: '{{ or (eq .Port "2812") (eq .Comm "monit") }}'
85
- tags: "mysql"
86
expr: '{{ or (eq .Port "3306") (eq .Comm "mysqld" "mariadbd") }}'
87
- tags: "nginx"
@@ -336,6 +338,13 @@ compose:
338
module: mongodb
339
name: local
340
uri: mongodb://{{.Address}}
341
+ - selector: "monit"
342
+ template: |
343
+ module: monit
344
+ name: local
345
+ url: http://{{.Address}}
346
+ username: admin
347
+ password: monit
348
- selector: "mysql"
349
template: |
350
- module: mysql
src/go/plugin/go.d/modules/init.go
+1
@@ -56,6 +56,7 @@ import (
56
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/megacli"
57
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/memcached"
58
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/mongodb"
59
+ _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/monit"
60
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/mysql"
61
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/nginx"
62
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/nginxplus"
src/go/plugin/go.d/modules/monit/charts.go
new
+91
@@ -0,0 +1,91 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package monit
4
+
5
+import (
6
+ "fmt"
7
+ "strings"
8
+
9
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
10
+)
11
+
12
+const (
13
+ prioServiceCheckStatus = module.Priority + iota
14
+ prioUptime
15
+)
16
+
17
+var baseCharts = module.Charts{
18
+ uptimeChart.Copy(),
19
+}
20
+
21
+var (
22
+ uptimeChart = module.Chart{
23
+ ID: "uptime",
24
+ Title: "Uptime",
25
+ Units: "seconds",
26
+ Fam: "uptime",
27
+ Ctx: "monit.uptime",
28
+ Priority: prioUptime,
29
+ Dims: module.Dims{
30
+ {ID: "uptime"},
31
+ },
32
+ }
33
+)
34
+
35
+var serviceCheckChartsTmpl = module.Charts{
36
+ serviceCheckStatusChartTmpl.Copy(),
37
+}
38
+
39
+var (
40
+ serviceCheckStatusChartTmpl = module.Chart{
41
+ ID: "service_check_type_%s_name_%s_status",
42
+ Title: "Service Check Status",
43
+ Units: "status",
44
+ Fam: "service status",
45
+ Ctx: "monit.service_check_status",
46
+ Priority: prioServiceCheckStatus,
47
+ Dims: module.Dims{
48
+ {ID: "service_check_type_%s_name_%s_status_ok", Name: "ok"},
49
+ {ID: "service_check_type_%s_name_%s_status_error", Name: "error"},
50
+ {ID: "service_check_type_%s_name_%s_status_initializing", Name: "initializing"},
51
+ {ID: "service_check_type_%s_name_%s_status_not_monitored", Name: "not_monitored"},
52
+ },
53
+ }
54
+)
55
+
56
+func (m *Monit) addServiceCheckCharts(svc statusServiceCheck, srv *statusServer) {
57
+ charts := serviceCheckChartsTmpl.Copy()
58
+
59
+ for _, chart := range *charts {
60
+ chart.ID = cleanChartId(fmt.Sprintf(chart.ID, svc.svcType(), svc.Name))
61
+ chart.Labels = []module.Label{
62
+ {Key: "server_hostname", Value: srv.LocalHostname},
63
+ {Key: "service_check_name", Value: svc.Name},
64
+ {Key: "service_check_type", Value: svc.svcType()},
65
+ }
66
+ for _, dim := range chart.Dims {
67
+ dim.ID = fmt.Sprintf(dim.ID, svc.svcType(), svc.Name)
68
+ }
69
+ }
70
+
71
+ if err := m.Charts().Add(*charts...); err != nil {
72
+ m.Warning(err)
73
+ }
74
+}
75
+
76
+func (m *Monit) removeServiceCharts(svc statusServiceCheck) {
77
+ px := fmt.Sprintf("service_check_type_%s_name_%s_", svc.svcType(), svc.Name)
78
+ px = cleanChartId(px)
79
+
80
+ for _, chart := range *m.Charts() {
81
+ if strings.HasPrefix(chart.ID, px) {
82
+ chart.MarkRemove()
83
+ chart.MarkNotCreated()
84
+ }
85
+ }
86
+}
87
+
88
+func cleanChartId(s string) string {
89
+ r := strings.NewReplacer(" ", "_", ".", "_", ",", "_")
90
+ return r.Replace(s)
91
+}
src/go/plugin/go.d/modules/monit/collect.go
new
+117
@@ -0,0 +1,117 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package monit
4
+
5
+import (
6
+ "encoding/xml"
7
+ "errors"
8
+ "fmt"
9
+ "io"
10
+ "net/http"
11
+ "net/url"
12
+
13
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
14
+
15
+ "golang.org/x/net/html/charset"
16
+)
17
+
18
+var (
19
+ urlPathStatus = "/_status"
20
+ urlQueryStatus = url.Values{"format": {"xml"}, "level": {"full"}}.Encode()
21
+)
22
+
23
+func (m *Monit) collect() (map[string]int64, error) {
24
+ mx := make(map[string]int64)
25
+
26
+ if err := m.collectStatus(mx); err != nil {
27
+ return nil, err
28
+ }
29
+
30
+ return mx, nil
31
+}
32
+
33
+func (m *Monit) collectStatus(mx map[string]int64) error {
34
+ status, err := m.fetchStatus()
35
+ if err != nil {
36
+ return err
37
+ }
38
+
39
+ if status.Server == nil {
40
+ // not Monit
41
+ return errors.New("invalid Monit status response: missing server data")
42
+ }
43
+
44
+ mx["uptime"] = status.Server.Uptime
45
+
46
+ seen := make(map[string]bool)
47
+
48
+ for _, svc := range status.Services {
49
+ seen[svc.id()] = true
50
+
51
+ if _, ok := m.seenServices[svc.id()]; !ok {
52
+ m.seenServices[svc.id()] = svc
53
+ m.addServiceCheckCharts(svc, status.Server)
54
+ }
55
+
56
+ px := fmt.Sprintf("service_check_type_%s_name_%s_status_", svc.svcType(), svc.Name)
57
+
58
+ for _, v := range []string{"not_monitored", "ok", "initializing", "error"} {
59
+ mx[px+v] = 0
60
+ if svc.status() == v {
61
+ mx[px+v] = 1
62
+ }
63
+ }
64
+ }
65
+
66
+ for id, svc := range m.seenServices {
67
+ if !seen[id] {
68
+ delete(m.seenServices, id)
69
+ m.removeServiceCharts(svc)
70
+ }
71
+ }
72
+
73
+ return nil
74
+}
75
+
76
+func (m *Monit) fetchStatus() (*monitStatus, error) {
77
+ req, err := web.NewHTTPRequestWithPath(m.Request, urlPathStatus)
78
+ if err != nil {
79
+ return nil, err
80
+ }
81
+ req.URL.RawQuery = urlQueryStatus
82
+
83
+ var status monitStatus
84
+ if err := m.doOKDecode(req, &status); err != nil {
85
+ return nil, err
86
+ }
87
+
88
+ return &status, nil
89
+}
90
+
91
+func (m *Monit) doOKDecode(req *http.Request, in interface{}) error {
92
+ resp, err := m.httpClient.Do(req)
93
+ if err != nil {
94
+ return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
95
+ }
96
+ defer closeBody(resp)
97
+
98
+ if resp.StatusCode != http.StatusOK {
99
+ return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
100
+ }
101
+
102
+ dec := xml.NewDecoder(resp.Body)
103
+ dec.CharsetReader = charset.NewReaderLabel
104
+
105
+ if err := dec.Decode(in); err != nil {
106
+ return fmt.Errorf("error on decoding XML response from '%s': %v", req.URL, err)
107
+ }
108
+
109
+ return nil
110
+}
111
+
112
+func closeBody(resp *http.Response) {
113
+ if resp != nil && resp.Body != nil {
114
+ _, _ = io.Copy(io.Discard, resp.Body)
115
+ _ = resp.Body.Close()
116
+ }
117
+}
src/go/plugin/go.d/modules/monit/config_schema.json
new
+185
@@ -0,0 +1,185 @@
1
+{
2
+ "jsonSchema": {
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "title": "Monit collector configuration.",
5
+ "type": "object",
6
+ "properties": {
7
+ "update_every": {
8
+ "title": "Update every",
9
+ "description": "Data collection interval, measured in seconds.",
10
+ "type": "integer",
11
+ "minimum": 1,
12
+ "default": 1
13
+ },
14
+ "url": {
15
+ "title": "URL",
16
+ "description": "The base URL of the Monit server.",
17
+ "type": "string",
18
+ "default": "http://127.0.0.1:2812",
19
+ "format": "uri"
20
+ },
21
+ "timeout": {
22
+ "title": "Timeout",
23
+ "description": "The timeout in seconds for the HTTP request.",
24
+ "type": "number",
25
+ "minimum": 0.5,
26
+ "default": 1
27
+ },
28
+ "not_follow_redirects": {
29
+ "title": "Not follow redirects",
30
+ "description": "If set, the client will not follow HTTP redirects automatically.",
31
+ "type": "boolean"
32
+ },
33
+ "username": {
34
+ "title": "Username",
35
+ "description": "The username for basic authentication.",
36
+ "type": "string",
37
+ "sensitive": true,
38
+ "default": "admin"
39
+ },
40
+ "password": {
41
+ "title": "Password",
42
+ "description": "The password for basic authentication.",
43
+ "type": "string",
44
+ "sensitive": true,
45
+ "default": "monit"
46
+ },
47
+ "proxy_url": {
48
+ "title": "Proxy URL",
49
+ "description": "The URL of the proxy server.",
50
+ "type": "string"
51
+ },
52
+ "proxy_username": {
53
+ "title": "Proxy username",
54
+ "description": "The username for proxy authentication.",
55
+ "type": "string",
56
+ "sensitive": true
57
+ },
58
+ "proxy_password": {
59
+ "title": "Proxy password",
60
+ "description": "The password for proxy authentication.",
61
+ "type": "string",
62
+ "sensitive": true
63
+ },
64
+ "headers": {
65
+ "title": "Headers",
66
+ "description": "Additional HTTP headers to include in the request.",
67
+ "type": [
68
+ "object",
69
+ "null"
70
+ ],
71
+ "additionalProperties": {
72
+ "type": "string"
73
+ }
74
+ },
75
+ "tls_skip_verify": {
76
+ "title": "Skip TLS verification",
77
+ "description": "If set, TLS certificate verification will be skipped.",
78
+ "type": "boolean"
79
+ },
80
+ "tls_ca": {
81
+ "title": "TLS CA",
82
+ "description": "The path to the CA certificate file for TLS verification.",
83
+ "type": "string",
84
+ "pattern": "^$|^/"
85
+ },
86
+ "tls_cert": {
87
+ "title": "TLS certificate",
88
+ "description": "The path to the client certificate file for TLS authentication.",
89
+ "type": "string",
90
+ "pattern": "^$|^/"
91
+ },
92
+ "tls_key": {
93
+ "title": "TLS key",
94
+ "description": "The path to the client key file for TLS authentication.",
95
+ "type": "string",
96
+ "pattern": "^$|^/"
97
+ },
98
+ "body": {
99
+ "title": "Body",
100
+ "type": "string"
101
+ },
102
+ "method": {
103
+ "title": "Method",
104
+ "type": "string"
105
+ }
106
+ },
107
+ "required": [
108
+ "url"
109
+ ],
110
+ "additionalProperties": false,
111
+ "patternProperties": {
112
+ "^name$": {}
113
+ }
114
+ },
115
+ "uiSchema": {
116
+ "uiOptions": {
117
+ "fullPage": true
118
+ },
119
+ "body": {
120
+ "ui:widget": "hidden"
121
+ },
122
+ "method": {
123
+ "ui:widget": "hidden"
124
+ },
125
+ "timeout": {
126
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
127
+ },
128
+ "username": {
129
+ "ui:widget": "password"
130
+ },
131
+ "proxy_username": {
132
+ "ui:widget": "password"
133
+ },
134
+ "password": {
135
+ "ui:widget": "password"
136
+ },
137
+ "proxy_password": {
138
+ "ui:widget": "password"
139
+ },
140
+ "ui:flavour": "tabs",
141
+ "ui:options": {
142
+ "tabs": [
143
+ {
144
+ "title": "Base",
145
+ "fields": [
146
+ "update_every",
147
+ "url",
148
+ "timeout",
149
+ "not_follow_redirects"
150
+ ]
151
+ },
152
+ {
153
+ "title": "Auth",
154
+ "fields": [
155
+ "username",
156
+ "password"
157
+ ]
158
+ },
159
+ {
160
+ "title": "TLS",
161
+ "fields": [
162
+ "tls_skip_verify",
163
+ "tls_ca",
164
+ "tls_cert",
165
+ "tls_key"
166
+ ]
167
+ },
168
+ {
169
+ "title": "Proxy",
170
+ "fields": [
171
+ "proxy_url",
172
+ "proxy_username",
173
+ "proxy_password"
174
+ ]
175
+ },
176
+ {
177
+ "title": "Headers",
178
+ "fields": [
179
+ "headers"
180
+ ]
181
+ }
182
+ ]
183
+ }
184
+ }
185
+}
src/go/plugin/go.d/modules/monit/metadata.yaml
new
+193
@@ -0,0 +1,193 @@
1
+plugin_name: go.d.plugin
2
+modules:
3
+ - meta:
4
+ id: collector-go.d.plugin-monit
5
+ plugin_name: go.d.plugin
6
+ module_name: monit
7
+ monitored_instance:
8
+ name: Monit
9
+ link: https://mmonit.com/monit/
10
+ categories:
11
+ - data-collection.synthetic-checks
12
+ icon_filename: monit.png
13
+ related_resources:
14
+ integrations:
15
+ list: []
16
+ alternative_monitored_instances: []
17
+ info_provided_to_referring_integrations:
18
+ description: ""
19
+ keywords:
20
+ - monit
21
+ - mmonit
22
+ - supervision tool
23
+ - monitrc
24
+ most_popular: false
25
+ overview:
26
+ data_collection:
27
+ metrics_description: |
28
+ This collector monitors status of Monit's service checks.
29
+ method_description: |
30
+ It sends HTTP requests to the Monit `/_status?format=xml&level=full` endpoint.
31
+ default_behavior:
32
+ auto_detection:
33
+ description: |
34
+ By default, it detects Monit instances running on localhost that are listening on port 2812.
35
+ On startup, it tries to collect metrics from:
36
+
37
+ - http://127.0.0.1:2812
38
+ limits:
39
+ description: ""
40
+ performance_impact:
41
+ description: ""
42
+ additional_permissions:
43
+ description: ""
44
+ multi_instance: true
45
+ supported_platforms:
46
+ include: []
47
+ exclude: []
48
+ setup:
49
+ prerequisites:
50
+ list:
51
+ - title: Enable TCP PORT
52
+ description:
53
+ See [Syntax for TCP port](https://mmonit.com/monit/documentation/monit.html#TCP-PORT) for details.
54
+ configuration:
55
+ file:
56
+ name: go.d/monit.conf
57
+ options:
58
+ description: |
59
+ The following options can be defined globally: update_every, autodetection_retry.
60
+ folding:
61
+ title: Config options
62
+ enabled: true
63
+ list:
64
+ - name: update_every
65
+ description: Data collection frequency.
66
+ default_value: 1
67
+ required: false
68
+ - name: autodetection_retry
69
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
70
+ default_value: 0
71
+ required: false
72
+ - name: url
73
+ description: Server URL.
74
+ default_value: http://127.0.0.1:2812
75
+ required: true
76
+ - name: timeout
77
+ description: HTTP request timeout.
78
+ default_value: 1
79
+ required: false
80
+ - name: username
81
+ description: Username for basic HTTP authentication.
82
+ default_value: "admin"
83
+ required: false
84
+ - name: password
85
+ description: Password for basic HTTP authentication.
86
+ default_value: "monit"
87
+ required: false
88
+ - name: proxy_url
89
+ description: Proxy URL.
90
+ default_value: ""
91
+ required: false
92
+ - name: proxy_username
93
+ description: Username for proxy basic HTTP authentication.
94
+ default_value: ""
95
+ required: false
96
+ - name: proxy_password
97
+ description: Password for proxy basic HTTP authentication.
98
+ default_value: ""
99
+ required: false
100
+ - name: method
101
+ description: HTTP request method.
102
+ default_value: GET
103
+ required: false
104
+ - name: body
105
+ description: HTTP request body.
106
+ default_value: ""
107
+ required: false
108
+ - name: headers
109
+ description: HTTP request headers.
110
+ default_value: ""
111
+ required: false
112
+ - name: not_follow_redirects
113
+ description: Redirect handling policy. Controls whether the client follows redirects.
114
+ default_value: false
115
+ required: false
116
+ - name: tls_skip_verify
117
+ description: Server certificate chain and hostname validation policy. Controls whether the client performs this check.
118
+ default_value: false
119
+ required: false
120
+ - name: tls_ca
121
+ description: Certification authority that the client uses when verifying the server's certificates.
122
+ default_value: ""
123
+ required: false
124
+ - name: tls_cert
125
+ description: Client TLS certificate.
126
+ default_value: ""
127
+ required: false
128
+ - name: tls_key
129
+ description: Client TLS key.
130
+ default_value: ""
131
+ required: false
132
+ examples:
133
+ folding:
134
+ title: Config
135
+ enabled: true
136
+ list:
137
+ - name: HTTP authentication
138
+ description: Basic HTTP authentication.
139
+ config: |
140
+ jobs:
141
+ - name: local
142
+ url: http://127.0.0.1:2812
143
+ username: admin
144
+ password: monit
145
+ - name: HTTPS with self-signed certificate
146
+ description: With enabled HTTPS and self-signed certificate.
147
+ config: |
148
+ jobs:
149
+ - name: local
150
+ url: http://127.0.0.1:2812
151
+ tls_skip_verify: yes
152
+ - name: Multi-instance
153
+ description: |
154
+ > **Note**: When you define multiple jobs, their names must be unique.
155
+
156
+ Collecting metrics from local and remote instances.
157
+ config: |
158
+ jobs:
159
+ - name: local
160
+ url: http://127.0.0.1:2812
161
+
162
+ - name: remote
163
+ url: http://192.0.2.1:2812
164
+ troubleshooting:
165
+ problems:
166
+ list: []
167
+ alerts: []
168
+ metrics:
169
+ folding:
170
+ title: Metrics
171
+ enabled: false
172
+ description: ""
173
+ availability: []
174
+ scopes:
175
+ - name: service
176
+ description: These metrics refer to the monitored Service.
177
+ labels:
178
+ - name: server_hostname
179
+ description: Hostname of the Monit server.
180
+ - name: service_check_name
181
+ description: Service check name.
182
+ - name: service_check_type
183
+ description: Service check type.
184
+ metrics:
185
+ - name: monit.service_check_status
186
+ description: Service Check Status
187
+ unit: status
188
+ chart_type: line
189
+ dimensions:
190
+ - name: ok
191
+ - name: error
192
+ - name: initializing
193
+ - name: not_monitored
src/go/plugin/go.d/modules/monit/monit.go
new
+117
@@ -0,0 +1,117 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package monit
4
+
5
+import (
6
+ _ "embed"
7
+ "errors"
8
+ "net/http"
9
+ "time"
10
+
11
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
12
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
13
+)
14
+
15
+//go:embed "config_schema.json"
16
+var configSchema string
17
+
18
+func init() {
19
+ module.Register("monit", module.Creator{
20
+ Create: func() module.Module { return New() },
21
+ JobConfigSchema: configSchema,
22
+ Config: func() any { return &Config{} },
23
+ })
24
+}
25
+
26
+func New() *Monit {
27
+ return &Monit{
28
+ Config: Config{
29
+ HTTP: web.HTTP{
30
+ Request: web.Request{
31
+ URL: "http://127.0.0.1:2812",
32
+ Username: "admin",
33
+ Password: "monit",
34
+ },
35
+ Client: web.Client{
36
+ Timeout: web.Duration(time.Second),
37
+ },
38
+ },
39
+ },
40
+ charts: baseCharts.Copy(),
41
+ seenServices: make(map[string]statusServiceCheck),
42
+ }
43
+}
44
+
45
+type Config struct {
46
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
47
+ web.HTTP `yaml:",inline" json:""`
48
+}
49
+
50
+type Monit struct {
51
+ module.Base
52
+ Config `yaml:",inline" json:""`
53
+
54
+ charts *module.Charts
55
+
56
+ httpClient *http.Client
57
+
58
+ seenServices map[string]statusServiceCheck
59
+}
60
+
61
+func (m *Monit) Configuration() any {
62
+ return m.Config
63
+}
64
+
65
+func (m *Monit) Init() error {
66
+ if m.URL == "" {
67
+ m.Error("config: monit url is required but not set")
68
+ return errors.New("config: missing URL")
69
+ }
70
+
71
+ httpClient, err := web.NewHTTPClient(m.Client)
72
+ if err != nil {
73
+ m.Errorf("init HTTP client: %v", err)
74
+ return err
75
+ }
76
+ m.httpClient = httpClient
77
+
78
+ m.Debugf("using URL %s", m.URL)
79
+ m.Debugf("using timeout: %s", m.Timeout)
80
+
81
+ return nil
82
+}
83
+
84
+func (m *Monit) Check() error {
85
+ mx, err := m.collect()
86
+ if err != nil {
87
+ m.Error(err)
88
+ return err
89
+ }
90
+ if len(mx) == 0 {
91
+ return errors.New("no metrics collected")
92
+
93
+ }
94
+ return nil
95
+}
96
+
97
+func (m *Monit) Charts() *module.Charts {
98
+ return m.charts
99
+}
100
+
101
+func (m *Monit) Collect() map[string]int64 {
102
+ mx, err := m.collect()
103
+ if err != nil {
104
+ m.Error(err)
105
+ }
106
+
107
+ if len(mx) == 0 {
108
+ return nil
109
+ }
110
+ return mx
111
+}
112
+
113
+func (m *Monit) Cleanup() {
114
+ if m.httpClient != nil {
115
+ m.httpClient.CloseIdleConnections()
116
+ }
117
+}
src/go/plugin/go.d/modules/monit/monit_test.go
new
+371
@@ -0,0 +1,371 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package monit
4
+
5
+import (
6
+ "net/http"
7
+ "net/http/httptest"
8
+ "os"
9
+ "testing"
10
+
11
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
12
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
13
+
14
+ "github.com/stretchr/testify/assert"
15
+ "github.com/stretchr/testify/require"
16
+)
17
+
18
+var (
19
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
20
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
21
+
22
+ dataStatus, _ = os.ReadFile("testdata/v5.33.0/status.xml")
23
+)
24
+
25
+func Test_testDataIsValid(t *testing.T) {
26
+ for name, data := range map[string][]byte{
27
+ "dataConfigJSON": dataConfigJSON,
28
+ "dataConfigYAML": dataConfigYAML,
29
+ "dataStatus": dataStatus,
30
+ } {
31
+ require.NotNil(t, data, name)
32
+
33
+ }
34
+}
35
+
36
+func TestMonit_ConfigurationSerialize(t *testing.T) {
37
+ module.TestConfigurationSerialize(t, &Monit{}, dataConfigJSON, dataConfigYAML)
38
+}
39
+
40
+func TestMonit_Init(t *testing.T) {
41
+ tests := map[string]struct {
42
+ wantFail bool
43
+ config Config
44
+ }{
45
+ "success with default": {
46
+ wantFail: false,
47
+ config: New().Config,
48
+ },
49
+ "fail when URL not set": {
50
+ wantFail: true,
51
+ config: Config{
52
+ HTTP: web.HTTP{
53
+ Request: web.Request{URL: ""},
54
+ },
55
+ },
56
+ },
57
+ }
58
+
59
+ for name, test := range tests {
60
+ t.Run(name, func(t *testing.T) {
61
+ monit := New()
62
+ monit.Config = test.config
63
+
64
+ if test.wantFail {
65
+ assert.Error(t, monit.Init())
66
+ } else {
67
+ assert.NoError(t, monit.Init())
68
+ }
69
+ })
70
+ }
71
+}
72
+
73
+func TestMonit_Check(t *testing.T) {
74
+ tests := map[string]struct {
75
+ wantFail bool
76
+ prepare func(t *testing.T) (monit *Monit, cleanup func())
77
+ }{
78
+ "success on valid response": {
79
+ wantFail: false,
80
+ prepare: caseOk,
81
+ },
82
+ "fail on unexpected XML response": {
83
+ wantFail: true,
84
+ prepare: caseUnexpectedXMLResponse,
85
+ },
86
+ "fail on invalid data response": {
87
+ wantFail: true,
88
+ prepare: caseInvalidDataResponse,
89
+ },
90
+ "fail on connection refused": {
91
+ wantFail: true,
92
+ prepare: caseConnectionRefused,
93
+ },
94
+ "fail on 404 response": {
95
+ wantFail: true,
96
+ prepare: case404,
97
+ },
98
+ }
99
+
100
+ for name, test := range tests {
101
+ t.Run(name, func(t *testing.T) {
102
+ monit, cleanup := test.prepare(t)
103
+ defer cleanup()
104
+
105
+ if test.wantFail {
106
+ assert.Error(t, monit.Check())
107
+ } else {
108
+ assert.NoError(t, monit.Check())
109
+ }
110
+ })
111
+ }
112
+}
113
+
114
+func TestMonit_Charts(t *testing.T) {
115
+ assert.NotNil(t, New().Charts())
116
+}
117
+
118
+func TestMonit_Collect(t *testing.T) {
119
+ tests := map[string]struct {
120
+ prepare func(t *testing.T) (monit *Monit, cleanup func())
121
+ wantNumOfCharts int
122
+ wantMetrics map[string]int64
123
+ }{
124
+ "success on valid response": {
125
+ prepare: caseOk,
126
+ wantNumOfCharts: len(baseCharts) + len(serviceCheckChartsTmpl)*25,
127
+ wantMetrics: map[string]int64{
128
+ "service_check_type_directory_name_directoryAlert_status_error": 1,
129
+ "service_check_type_directory_name_directoryAlert_status_initializing": 0,
130
+ "service_check_type_directory_name_directoryAlert_status_not_monitored": 0,
131
+ "service_check_type_directory_name_directoryAlert_status_ok": 0,
132
+ "service_check_type_directory_name_directoryDisabled_status_error": 0,
133
+ "service_check_type_directory_name_directoryDisabled_status_initializing": 0,
134
+ "service_check_type_directory_name_directoryDisabled_status_not_monitored": 1,
135
+ "service_check_type_directory_name_directoryDisabled_status_ok": 0,
136
+ "service_check_type_directory_name_directoryNotExists_status_error": 1,
137
+ "service_check_type_directory_name_directoryNotExists_status_initializing": 0,
138
+ "service_check_type_directory_name_directoryNotExists_status_not_monitored": 0,
139
+ "service_check_type_directory_name_directoryNotExists_status_ok": 0,
140
+ "service_check_type_directory_name_directoryOk_status_error": 0,
141
+ "service_check_type_directory_name_directoryOk_status_initializing": 0,
142
+ "service_check_type_directory_name_directoryOk_status_not_monitored": 0,
143
+ "service_check_type_directory_name_directoryOk_status_ok": 1,
144
+ "service_check_type_file_name_fileAlert_status_error": 1,
145
+ "service_check_type_file_name_fileAlert_status_initializing": 0,
146
+ "service_check_type_file_name_fileAlert_status_not_monitored": 0,
147
+ "service_check_type_file_name_fileAlert_status_ok": 0,
148
+ "service_check_type_file_name_fileDisabled_status_error": 0,
149
+ "service_check_type_file_name_fileDisabled_status_initializing": 0,
150
+ "service_check_type_file_name_fileDisabled_status_not_monitored": 1,
151
+ "service_check_type_file_name_fileDisabled_status_ok": 0,
152
+ "service_check_type_file_name_fileNotExists_status_error": 1,
153
+ "service_check_type_file_name_fileNotExists_status_initializing": 0,
154
+ "service_check_type_file_name_fileNotExists_status_not_monitored": 0,
155
+ "service_check_type_file_name_fileNotExists_status_ok": 0,
156
+ "service_check_type_file_name_fileOk_status_error": 0,
157
+ "service_check_type_file_name_fileOk_status_initializing": 0,
158
+ "service_check_type_file_name_fileOk_status_not_monitored": 0,
159
+ "service_check_type_file_name_fileOk_status_ok": 1,
160
+ "service_check_type_filesystem_name_filesystemAlert_status_error": 1,
161
+ "service_check_type_filesystem_name_filesystemAlert_status_initializing": 0,
162
+ "service_check_type_filesystem_name_filesystemAlert_status_not_monitored": 0,
163
+ "service_check_type_filesystem_name_filesystemAlert_status_ok": 0,
164
+ "service_check_type_filesystem_name_filesystemDisabled_status_error": 0,
165
+ "service_check_type_filesystem_name_filesystemDisabled_status_initializing": 0,
166
+ "service_check_type_filesystem_name_filesystemDisabled_status_not_monitored": 1,
167
+ "service_check_type_filesystem_name_filesystemDisabled_status_ok": 0,
168
+ "service_check_type_filesystem_name_filesystemNotExists_status_error": 1,
169
+ "service_check_type_filesystem_name_filesystemNotExists_status_initializing": 0,
170
+ "service_check_type_filesystem_name_filesystemNotExists_status_not_monitored": 0,
171
+ "service_check_type_filesystem_name_filesystemNotExists_status_ok": 0,
172
+ "service_check_type_filesystem_name_filsystemOk_status_error": 0,
173
+ "service_check_type_filesystem_name_filsystemOk_status_initializing": 0,
174
+ "service_check_type_filesystem_name_filsystemOk_status_not_monitored": 0,
175
+ "service_check_type_filesystem_name_filsystemOk_status_ok": 1,
176
+ "service_check_type_host_name_hostAlert_status_error": 1,
177
+ "service_check_type_host_name_hostAlert_status_initializing": 0,
178
+ "service_check_type_host_name_hostAlert_status_not_monitored": 0,
179
+ "service_check_type_host_name_hostAlert_status_ok": 0,
180
+ "service_check_type_host_name_hostDisabled_status_error": 0,
181
+ "service_check_type_host_name_hostDisabled_status_initializing": 0,
182
+ "service_check_type_host_name_hostDisabled_status_not_monitored": 1,
183
+ "service_check_type_host_name_hostDisabled_status_ok": 0,
184
+ "service_check_type_host_name_hostNotExists_status_error": 1,
185
+ "service_check_type_host_name_hostNotExists_status_initializing": 0,
186
+ "service_check_type_host_name_hostNotExists_status_not_monitored": 0,
187
+ "service_check_type_host_name_hostNotExists_status_ok": 0,
188
+ "service_check_type_host_name_hostOk_status_error": 0,
189
+ "service_check_type_host_name_hostOk_status_initializing": 0,
190
+ "service_check_type_host_name_hostOk_status_not_monitored": 0,
191
+ "service_check_type_host_name_hostOk_status_ok": 1,
192
+ "service_check_type_network_name_networkAlert_status_error": 1,
193
+ "service_check_type_network_name_networkAlert_status_initializing": 0,
194
+ "service_check_type_network_name_networkAlert_status_not_monitored": 0,
195
+ "service_check_type_network_name_networkAlert_status_ok": 0,
196
+ "service_check_type_network_name_networkDisabled_status_error": 0,
197
+ "service_check_type_network_name_networkDisabled_status_initializing": 0,
198
+ "service_check_type_network_name_networkDisabled_status_not_monitored": 1,
199
+ "service_check_type_network_name_networkDisabled_status_ok": 0,
200
+ "service_check_type_network_name_networkNotExists_status_error": 1,
201
+ "service_check_type_network_name_networkNotExists_status_initializing": 0,
202
+ "service_check_type_network_name_networkNotExists_status_not_monitored": 0,
203
+ "service_check_type_network_name_networkNotExists_status_ok": 0,
204
+ "service_check_type_network_name_networkOk_status_error": 0,
205
+ "service_check_type_network_name_networkOk_status_initializing": 0,
206
+ "service_check_type_network_name_networkOk_status_not_monitored": 0,
207
+ "service_check_type_network_name_networkOk_status_ok": 1,
208
+ "service_check_type_process_name_processAlert_status_error": 1,
209
+ "service_check_type_process_name_processAlert_status_initializing": 0,
210
+ "service_check_type_process_name_processAlert_status_not_monitored": 0,
211
+ "service_check_type_process_name_processAlert_status_ok": 0,
212
+ "service_check_type_process_name_processDisabled_status_error": 0,
213
+ "service_check_type_process_name_processDisabled_status_initializing": 0,
214
+ "service_check_type_process_name_processDisabled_status_not_monitored": 1,
215
+ "service_check_type_process_name_processDisabled_status_ok": 0,
216
+ "service_check_type_process_name_processNotExists_status_error": 1,
217
+ "service_check_type_process_name_processNotExists_status_initializing": 0,
218
+ "service_check_type_process_name_processNotExists_status_not_monitored": 0,
219
+ "service_check_type_process_name_processNotExists_status_ok": 0,
220
+ "service_check_type_process_name_processOk_status_error": 0,
221
+ "service_check_type_process_name_processOk_status_initializing": 0,
222
+ "service_check_type_process_name_processOk_status_not_monitored": 0,
223
+ "service_check_type_process_name_processOk_status_ok": 1,
224
+ "service_check_type_system_name_pve-deb-work_status_error": 0,
225
+ "service_check_type_system_name_pve-deb-work_status_initializing": 0,
226
+ "service_check_type_system_name_pve-deb-work_status_not_monitored": 0,
227
+ "service_check_type_system_name_pve-deb-work_status_ok": 1,
228
+ "uptime": 33,
229
+ },
230
+ },
231
+ "fail on unexpected XML response": {
232
+ prepare: caseUnexpectedXMLResponse,
233
+ wantNumOfCharts: len(baseCharts),
234
+ wantMetrics: nil,
235
+ },
236
+ "fail on invalid data response": {
237
+ prepare: caseInvalidDataResponse,
238
+ wantNumOfCharts: len(baseCharts),
239
+ wantMetrics: nil,
240
+ },
241
+ "fail on connection refused": {
242
+ prepare: caseConnectionRefused,
243
+ wantNumOfCharts: len(baseCharts),
244
+ wantMetrics: nil,
245
+ },
246
+ "fail on 404 response": {
247
+ prepare: case404,
248
+ wantNumOfCharts: len(baseCharts),
249
+ wantMetrics: nil,
250
+ },
251
+ }
252
+
253
+ for name, test := range tests {
254
+ t.Run(name, func(t *testing.T) {
255
+ monit, cleanup := test.prepare(t)
256
+ defer cleanup()
257
+
258
+ _ = monit.Check()
259
+
260
+ mx := monit.Collect()
261
+
262
+ require.Equal(t, test.wantMetrics, mx)
263
+
264
+ if len(test.wantMetrics) > 0 {
265
+ module.TestMetricsHasAllChartsDims(t, monit.Charts(), mx)
266
+ assert.Equal(t, test.wantNumOfCharts, len(*monit.Charts()), "want number of charts")
267
+ }
268
+ })
269
+ }
270
+}
271
+
272
+func caseOk(t *testing.T) (*Monit, func()) {
273
+ t.Helper()
274
+ srv := httptest.NewServer(http.HandlerFunc(
275
+ func(w http.ResponseWriter, r *http.Request) {
276
+ if r.URL.Path != urlPathStatus || r.URL.RawQuery != urlQueryStatus {
277
+ w.WriteHeader(http.StatusNotFound)
278
+ return
279
+ }
280
+ _, _ = w.Write(dataStatus)
281
+ }))
282
+ monit := New()
283
+ monit.URL = srv.URL
284
+ require.NoError(t, monit.Init())
285
+
286
+ return monit, srv.Close
287
+}
288
+
289
+func caseUnexpectedXMLResponse(t *testing.T) (*Monit, func()) {
290
+ t.Helper()
291
+ data := `<?xml version="1.0" encoding="UTF-8"?>
292
+<Response>
293
+ <Status>
294
+ <Code>200</Code>
295
+ <Message>Success</Message>
296
+ </Status>
297
+ <Data>
298
+ <User>
299
+ <ID>12345</ID>
300
+ <Name>John Doe</Name>
301
+ <Email>johndoe@example.com</Email>
302
+ <Roles>
303
+ <Role>Admin</Role>
304
+ <Role>User</Role>
305
+ </Roles>
306
+ </User>
307
+ <Order>
308
+ <OrderID>98765</OrderID>
309
+ <Date>2024-08-15</Date>
310
+ <Items>
311
+ <Item>
312
+ <Name>Widget A</Name>
313
+ <Quantity>2</Quantity>
314
+ <Price>19.99</Price>
315
+ </Item>
316
+ <Item>
317
+ <Name>Gadget B</Name>
318
+ <Quantity>1</Quantity>
319
+ <Price>99.99</Price>
320
+ </Item>
321
+ </Items>
322
+ <Total>139.97</Total>
323
+ </Order>
324
+ </Data>
325
+</Response>
326
+`
327
+ srv := httptest.NewServer(http.HandlerFunc(
328
+ func(w http.ResponseWriter, r *http.Request) {
329
+ _, _ = w.Write([]byte(data))
330
+ }))
331
+ monit := New()
332
+ monit.URL = srv.URL
333
+ require.NoError(t, monit.Init())
334
+
335
+ return monit, srv.Close
336
+}
337
+
338
+func caseInvalidDataResponse(t *testing.T) (*Monit, func()) {
339
+ t.Helper()
340
+ srv := httptest.NewServer(http.HandlerFunc(
341
+ func(w http.ResponseWriter, r *http.Request) {
342
+ _, _ = w.Write([]byte("hello and\n goodbye"))
343
+ }))
344
+ monit := New()
345
+ monit.URL = srv.URL
346
+ require.NoError(t, monit.Init())
347
+
348
+ return monit, srv.Close
349
+}
350
+
351
+func caseConnectionRefused(t *testing.T) (*Monit, func()) {
352
+ t.Helper()
353
+ monit := New()
354
+ monit.URL = "http://127.0.0.1:65001"
355
+ require.NoError(t, monit.Init())
356
+
357
+ return monit, func() {}
358
+}
359
+
360
+func case404(t *testing.T) (*Monit, func()) {
361
+ t.Helper()
362
+ srv := httptest.NewServer(http.HandlerFunc(
363
+ func(w http.ResponseWriter, r *http.Request) {
364
+ w.WriteHeader(http.StatusNotFound)
365
+ }))
366
+ monit := New()
367
+ monit.URL = srv.URL
368
+ require.NoError(t, monit.Init())
369
+
370
+ return monit, srv.Close
371
+}
src/go/plugin/go.d/modules/monit/status.go
new
+153
@@ -0,0 +1,153 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package monit
4
+
5
+// status_xml(): https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/http/xml.c#lines-631
6
+type monitStatus struct {
7
+ Server *statusServer `xml:"server"`
8
+ Services []statusServiceCheck `xml:"service"`
9
+}
10
+
11
+type statusServer struct {
12
+ ID string `xml:"id"`
13
+ Version string `xml:"version"`
14
+ Uptime int64 `xml:"uptime"`
15
+ LocalHostname string `xml:"localhostname"`
16
+}
17
+
18
+// status_service(): https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/http/xml.c#lines-196
19
+// struct Service_T: https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/monit.h#lines-1212
20
+type statusServiceCheck struct {
21
+ Type string `xml:"type,attr"`
22
+ Name string `xml:"name"`
23
+
24
+ Status int `xml:"status"` // Error flags bitmap
25
+
26
+ // https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/monit.h#lines-269
27
+ MonitoringStatus int `xml:"monitor"`
28
+
29
+ // https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/monit.h#lines-254
30
+ MonitorMode int `xml:"monitormode"`
31
+
32
+ // https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/monit.h#lines-261
33
+ OnReboot int `xml:"onreboot"`
34
+
35
+ // https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/monit.h#lines-248
36
+ PendingAction int `xml:"pendingaction"`
37
+}
38
+
39
+func (s *statusServiceCheck) id() string {
40
+ return s.svcType() + ":" + s.Name
41
+}
42
+
43
+func (s *statusServiceCheck) svcType() string {
44
+ // See enum Service_Type https://bitbucket.org/tildeslash/monit/src/master/src/monit.h
45
+
46
+ switch s.Type {
47
+ case "0":
48
+ return "filesystem"
49
+ case "1":
50
+ return "directory"
51
+ case "2":
52
+ return "file"
53
+ case "3":
54
+ return "process"
55
+ case "4":
56
+ return "host"
57
+ case "5":
58
+ return "system"
59
+ case "6":
60
+ return "fifo"
61
+ case "7":
62
+ return "program"
63
+ case "8":
64
+ return "network"
65
+ default:
66
+ return "unknown"
67
+ }
68
+}
69
+
70
+func (s *statusServiceCheck) status() string {
71
+ // https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/http/cervlet.c#lines-2866
72
+
73
+ switch st := s.monitoringStatus(); st {
74
+ case "not_monitored", "initializing":
75
+ return st
76
+ default:
77
+ if s.Status != 0 {
78
+ return "error"
79
+ }
80
+ return "ok"
81
+ }
82
+}
83
+
84
+func (s *statusServiceCheck) monitoringStatus() string {
85
+ switch s.MonitoringStatus {
86
+ case 0:
87
+ return "not_monitored"
88
+ case 1:
89
+ return "monitored"
90
+ case 2:
91
+ return "initializing"
92
+ case 4:
93
+ return "waiting"
94
+ default:
95
+ return "unknown"
96
+ }
97
+}
98
+
99
+func (s *statusServiceCheck) monitorMode() string {
100
+ switch s.MonitorMode {
101
+ case 0:
102
+ return "active"
103
+ case 1:
104
+ return "passive"
105
+ default:
106
+ return "unknown"
107
+ }
108
+}
109
+
110
+func (s *statusServiceCheck) onReboot() string {
111
+ switch s.OnReboot {
112
+ case 0:
113
+ return "start"
114
+ case 1:
115
+ return "no_start"
116
+ default:
117
+ return "unknown"
118
+ }
119
+}
120
+
121
+func (s *statusServiceCheck) pendingAction() string {
122
+ switch s.PendingAction {
123
+ case 0:
124
+ return "ignored"
125
+ case 1:
126
+ return "alert"
127
+ case 2:
128
+ return "restart"
129
+ case 3:
130
+ return "stop"
131
+ case 4:
132
+ return "exec"
133
+ case 5:
134
+ return "unmonitor"
135
+ case 6:
136
+ return "start"
137
+ case 7:
138
+ return "monitor"
139
+ default:
140
+ return "unknown"
141
+ }
142
+}
143
+
144
+func (s *statusServiceCheck) hasServiceStatus() bool {
145
+ // https://bitbucket.org/tildeslash/monit/src/5467d37d70c3c63c5760cddb93831bde4e17c14b/src/util.c#lines-1721
146
+
147
+ const eventNonExist = 512
148
+ const eventData = 2048
149
+
150
+ return s.monitoringStatus() == "monitored" &&
151
+ !(s.Status&eventNonExist != 0) &&
152
+ !(s.Status&eventData != 0)
153
+}
src/go/plugin/go.d/modules/monit/testdata/config.json
new
+20
@@ -0,0 +1,20 @@
1
+{
2
+ "update_every": 123,
3
+ "url": "ok",
4
+ "body": "ok",
5
+ "method": "ok",
6
+ "headers": {
7
+ "ok": "ok"
8
+ },
9
+ "username": "ok",
10
+ "password": "ok",
11
+ "proxy_url": "ok",
12
+ "proxy_username": "ok",
13
+ "proxy_password": "ok",
14
+ "timeout": 123.123,
15
+ "not_follow_redirects": true,
16
+ "tls_ca": "ok",
17
+ "tls_cert": "ok",
18
+ "tls_key": "ok",
19
+ "tls_skip_verify": true
20
+}
src/go/plugin/go.d/modules/monit/testdata/config.yaml
new
+17
@@ -0,0 +1,17 @@
1
+update_every: 123
2
+url: "ok"
3
+body: "ok"
4
+method: "ok"
5
+headers:
6
+ ok: "ok"
7
+username: "ok"
8
+password: "ok"
9
+proxy_url: "ok"
10
+proxy_username: "ok"
11
+proxy_password: "ok"
12
+timeout: 123.123
13
+not_follow_redirects: yes
14
+tls_ca: "ok"
15
+tls_cert: "ok"
16
+tls_key: "ok"
17
+tls_skip_verify: yes
src/go/plugin/go.d/modules/monit/testdata/v5.33.0/status.xml
new
+688
@@ -0,0 +1,688 @@
1
+<?xml version="1.0" encoding="ISO-8859-1"?>
2
+<monit>
3
+ <server>
4
+ <id>309dc5d56ccd5964cef9b42d1d8305e7</id>
5
+ <incarnation>1723810534</incarnation>
6
+ <version>5.33.0</version>
7
+ <uptime>33</uptime>
8
+ <poll>120</poll>
9
+ <startdelay>0</startdelay>
10
+ <localhostname>pve-deb-work</localhostname>
11
+ <controlfile>/etc/monit/monitrc</controlfile>
12
+ <httpd>
13
+ <address>127.0.0.1</address>
14
+ <port>2812</port>
15
+ <ssl>0</ssl>
16
+ </httpd>
17
+ </server>
18
+ <platform>
19
+ <name>Linux</name>
20
+ <release>6.1.0-23-amd64</release>
21
+ <version>#1 SMP PREEMPT_DYNAMIC Debian 6.1.99-1 (2024-07-15)</version>
22
+ <machine>x86_64</machine>
23
+ <cpu>16</cpu>
24
+ <memory>32864100</memory>
25
+ <swap>262140</swap>
26
+ </platform>
27
+ <service type="3">
28
+ <name>processOk</name>
29
+ <collected_sec>1723810534</collected_sec>
30
+ <collected_usec>86510</collected_usec>
31
+ <status>0</status>
32
+ <status_hint>0</status_hint>
33
+ <monitor>1</monitor>
34
+ <monitormode>0</monitormode>
35
+ <onreboot>0</onreboot>
36
+ <pendingaction>0</pendingaction>
37
+ <pid>843</pid>
38
+ <ppid>1</ppid>
39
+ <uid>0</uid>
40
+ <euid>0</euid>
41
+ <gid>0</gid>
42
+ <uptime>66112</uptime>
43
+ <threads>1</threads>
44
+ <children>2</children>
45
+ <memory>
46
+ <percent>0.0</percent>
47
+ <percenttotal>0.1</percenttotal>
48
+ <kilobyte>5036</kilobyte>
49
+ <kilobytetotal>34156</kilobytetotal>
50
+ </memory>
51
+ <cpu>
52
+ <percent>-1.0</percent>
53
+ <percenttotal>-1.0</percenttotal>
54
+ </cpu>
55
+ <filedescriptors>
56
+ <open>9</open>
57
+ <opentotal>34</opentotal>
58
+ <limit>
59
+ <soft>8192</soft>
60
+ <hard>8192</hard>
61
+ </limit>
62
+ </filedescriptors>
63
+ <read>
64
+ <bytesgeneric>
65
+ <count>0</count>
66
+ <total>1733465</total>
67
+ </bytesgeneric>
68
+ <bytes>
69
+ <count>0</count>
70
+ <total>135168</total>
71
+ </bytes>
72
+ <operations>
73
+ <count>0</count>
74
+ <total>23145</total>
75
+ </operations>
76
+ </read>
77
+ <write>
78
+ <bytesgeneric>
79
+ <count>0</count>
80
+ <total>8842272</total>
81
+ </bytesgeneric>
82
+ <bytes>
83
+ <count>0</count>
84
+ <total>9150464</total>
85
+ </bytes>
86
+ <operations>
87
+ <count>0</count>
88
+ <total>22890</total>
89
+ </operations>
90
+ </write>
91
+ </service>
92
+ <service type="3">
93
+ <name>processDisabled</name>
94
+ <collected_sec>1723810534</collected_sec>
95
+ <collected_usec>68402</collected_usec>
96
+ <status>0</status>
97
+ <status_hint>0</status_hint>
98
+ <monitor>0</monitor>
99
+ <monitormode>0</monitormode>
100
+ <onreboot>0</onreboot>
101
+ <pendingaction>0</pendingaction>
102
+ </service>
103
+ <service type="3">
104
+ <name>processAlert</name>
105
+ <collected_sec>1723810534</collected_sec>
106
+ <collected_usec>86548</collected_usec>
107
+ <status>2</status>
108
+ <status_hint>0</status_hint>
109
+ <monitor>1</monitor>
110
+ <monitormode>0</monitormode>
111
+ <onreboot>0</onreboot>
112
+ <pendingaction>0</pendingaction>
113
+ <pid>843</pid>
114
+ <ppid>1</ppid>
115
+ <uid>0</uid>
116
+ <euid>0</euid>
117
+ <gid>0</gid>
118
+ <uptime>66112</uptime>
119
+ <threads>1</threads>
120
+ <children>2</children>
121
+ <memory>
122
+ <percent>0.0</percent>
123
+ <percenttotal>0.1</percenttotal>
124
+ <kilobyte>5036</kilobyte>
125
+ <kilobytetotal>34156</kilobytetotal>
126
+ </memory>
127
+ <cpu>
128
+ <percent>-1.0</percent>
129
+ <percenttotal>-1.0</percenttotal>
130
+ </cpu>
131
+ <filedescriptors>
132
+ <open>9</open>
133
+ <opentotal>34</opentotal>
134
+ <limit>
135
+ <soft>8192</soft>
136
+ <hard>8192</hard>
137
+ </limit>
138
+ </filedescriptors>
139
+ <read>
140
+ <bytesgeneric>
141
+ <count>0</count>
142
+ <total>1733465</total>
143
+ </bytesgeneric>
144
+ <bytes>
145
+ <count>0</count>
146
+ <total>135168</total>
147
+ </bytes>
148
+ <operations>
149
+ <count>0</count>
150
+ <total>23145</total>
151
+ </operations>
152
+ </read>
153
+ <write>
154
+ <bytesgeneric>
155
+ <count>0</count>
156
+ <total>8842272</total>
157
+ </bytesgeneric>
158
+ <bytes>
159
+ <count>0</count>
160
+ <total>9150464</total>
161
+ </bytes>
162
+ <operations>
163
+ <count>0</count>
164
+ <total>22890</total>
165
+ </operations>
166
+ </write>
167
+ </service>
168
+ <service type="3">
169
+ <name>processNotExists</name>
170
+ <collected_sec>1723810534</collected_sec>
171
+ <collected_usec>86595</collected_usec>
172
+ <status>4608</status>
173
+ <status_hint>0</status_hint>
174
+ <monitor>1</monitor>
175
+ <monitormode>0</monitormode>
176
+ <onreboot>0</onreboot>
177
+ <pendingaction>0</pendingaction>
178
+ </service>
179
+ <service type="0">
180
+ <name>filsystemOk</name>
181
+ <collected_sec>1723810534</collected_sec>
182
+ <collected_usec>86891</collected_usec>
183
+ <status>0</status>
184
+ <status_hint>0</status_hint>
185
+ <monitor>1</monitor>
186
+ <monitormode>0</monitormode>
187
+ <onreboot>0</onreboot>
188
+ <pendingaction>0</pendingaction>
189
+ <fstype>ext2</fstype>
190
+ <fsflags>rw,relatime</fsflags>
191
+ <mode>660</mode>
192
+ <uid>0</uid>
193
+ <gid>6</gid>
194
+ <block>
195
+ <percent>19.6</percent>
196
+ <usage>92.0</usage>
197
+ <total>469.4</total>
198
+ </block>
199
+ <inode>
200
+ <percent>0.3</percent>
201
+ <usage>356</usage>
202
+ <total>124928</total>
203
+ </inode>
204
+ <read>
205
+ <bytes>
206
+ <count>0</count>
207
+ <total>5706752</total>
208
+ </bytes>
209
+ <operations>
210
+ <count>0</count>
211
+ <total>210</total>
212
+ </operations>
213
+ </read>
214
+ <write>
215
+ <bytes>
216
+ <count>0</count>
217
+ <total>1024</total>
218
+ </bytes>
219
+ <operations>
220
+ <count>0</count>
221
+ <total>1</total>
222
+ </operations>
223
+ </write>
224
+ <servicetime>
225
+ <read>0.000</read>
226
+ <write>0.000</write>
227
+ </servicetime>
228
+ </service>
229
+ <service type="0">
230
+ <name>filesystemDisabled</name>
231
+ <collected_sec>1723810534</collected_sec>
232
+ <collected_usec>68613</collected_usec>
233
+ <status>0</status>
234
+ <status_hint>0</status_hint>
235
+ <monitor>0</monitor>
236
+ <monitormode>0</monitormode>
237
+ <onreboot>0</onreboot>
238
+ <pendingaction>0</pendingaction>
239
+ </service>
240
+ <service type="0">
241
+ <name>filesystemAlert</name>
242
+ <collected_sec>1723810534</collected_sec>
243
+ <collected_usec>87124</collected_usec>
244
+ <status>384</status>
245
+ <status_hint>0</status_hint>
246
+ <monitor>1</monitor>
247
+ <monitormode>0</monitormode>
248
+ <onreboot>0</onreboot>
249
+ <pendingaction>0</pendingaction>
250
+ <fstype>ext2</fstype>
251
+ <fsflags>rw,relatime</fsflags>
252
+ <mode>660</mode>
253
+ <uid>0</uid>
254
+ <gid>6</gid>
255
+ <block>
256
+ <percent>19.6</percent>
257
+ <usage>92.0</usage>
258
+ <total>469.4</total>
259
+ </block>
260
+ <inode>
261
+ <percent>0.3</percent>
262
+ <usage>356</usage>
263
+ <total>124928</total>
264
+ </inode>
265
+ <read>
266
+ <bytes>
267
+ <count>0</count>
268
+ <total>5706752</total>
269
+ </bytes>
270
+ <operations>
271
+ <count>0</count>
272
+ <total>210</total>
273
+ </operations>
274
+ </read>
275
+ <write>
276
+ <bytes>
277
+ <count>0</count>
278
+ <total>1024</total>
279
+ </bytes>
280
+ <operations>
281
+ <count>0</count>
282
+ <total>1</total>
283
+ </operations>
284
+ </write>
285
+ <servicetime>
286
+ <read>0.000</read>
287
+ <write>0.000</write>
288
+ </servicetime>
289
+ </service>
290
+ <service type="0">
291
+ <name>filesystemNotExists</name>
292
+ <collected_sec>1723810534</collected_sec>
293
+ <collected_usec>87334</collected_usec>
294
+ <status>512</status>
295
+ <status_hint>0</status_hint>
296
+ <monitor>1</monitor>
297
+ <monitormode>0</monitormode>
298
+ <onreboot>0</onreboot>
299
+ <pendingaction>0</pendingaction>
300
+ </service>
301
+ <service type="2">
302
+ <name>fileOk</name>
303
+ <collected_sec>1723810534</collected_sec>
304
+ <collected_usec>87339</collected_usec>
305
+ <status>0</status>
306
+ <status_hint>0</status_hint>
307
+ <monitor>1</monitor>
308
+ <monitormode>0</monitormode>
309
+ <onreboot>0</onreboot>
310
+ <pendingaction>0</pendingaction>
311
+ <mode>755</mode>
312
+ <uid>0</uid>
313
+ <gid>0</gid>
314
+ <timestamps>
315
+ <access>1723744256</access>
316
+ <change>1723744256</change>
317
+ <modify>1723744256</modify>
318
+ </timestamps>
319
+ <size>84820392</size>
320
+ </service>
321
+ <service type="2">
322
+ <name>fileDisabled</name>
323
+ <collected_sec>1723810534</collected_sec>
324
+ <collected_usec>68835</collected_usec>
325
+ <status>0</status>
326
+ <status_hint>0</status_hint>
327
+ <monitor>0</monitor>
328
+ <monitormode>0</monitormode>
329
+ <onreboot>0</onreboot>
330
+ <pendingaction>0</pendingaction>
331
+ </service>
332
+ <service type="2">
333
+ <name>fileAlert</name>
334
+ <collected_sec>1723810534</collected_sec>
335
+ <collected_usec>87356</collected_usec>
336
+ <status>384</status>
337
+ <status_hint>0</status_hint>
338
+ <monitor>1</monitor>
339
+ <monitormode>0</monitormode>
340
+ <onreboot>0</onreboot>
341
+ <pendingaction>0</pendingaction>
342
+ <mode>755</mode>
343
+ <uid>0</uid>
344
+ <gid>0</gid>
345
+ <timestamps>
346
+ <access>1723744256</access>
347
+ <change>1723744256</change>
348
+ <modify>1723744256</modify>
349
+ </timestamps>
350
+ <size>84820392</size>
351
+ </service>
352
+ <service type="2">
353
+ <name>fileNotExists</name>
354
+ <collected_sec>1723810534</collected_sec>
355
+ <collected_usec>87371</collected_usec>
356
+ <status>512</status>
357
+ <status_hint>0</status_hint>
358
+ <monitor>1</monitor>
359
+ <monitormode>0</monitormode>
360
+ <onreboot>0</onreboot>
361
+ <pendingaction>0</pendingaction>
362
+ </service>
363
+ <service type="1">
364
+ <name>directoryOk</name>
365
+ <collected_sec>1723810534</collected_sec>
366
+ <collected_usec>87375</collected_usec>
367
+ <status>0</status>
368
+ <status_hint>0</status_hint>
369
+ <monitor>1</monitor>
370
+ <monitormode>0</monitormode>
371
+ <onreboot>0</onreboot>
372
+ <pendingaction>0</pendingaction>
373
+ <mode>775</mode>
374
+ <uid>0</uid>
375
+ <gid>0</gid>
376
+ <timestamps>
377
+ <access>1723740545</access>
378
+ <change>1720694060</change>
379
+ <modify>1720694060</modify>
380
+ </timestamps>
381
+ </service>
382
+ <service type="1">
383
+ <name>directoryDisabled</name>
384
+ <collected_sec>1723810534</collected_sec>
385
+ <collected_usec>68957</collected_usec>
386
+ <status>0</status>
387
+ <status_hint>0</status_hint>
388
+ <monitor>0</monitor>
389
+ <monitormode>0</monitormode>
390
+ <onreboot>0</onreboot>
391
+ <pendingaction>0</pendingaction>
392
+ </service>
393
+ <service type="1">
394
+ <name>directoryAlert</name>
395
+ <collected_sec>1723810534</collected_sec>
396
+ <collected_usec>87385</collected_usec>
397
+ <status>64</status>
398
+ <status_hint>0</status_hint>
399
+ <monitor>1</monitor>
400
+ <monitormode>0</monitormode>
401
+ <onreboot>0</onreboot>
402
+ <pendingaction>0</pendingaction>
403
+ <mode>775</mode>
404
+ <uid>0</uid>
405
+ <gid>0</gid>
406
+ <timestamps>
407
+ <access>1723740545</access>
408
+ <change>1720694060</change>
409
+ <modify>1720694060</modify>
410
+ </timestamps>
411
+ </service>
412
+ <service type="1">
413
+ <name>directoryNotExists</name>
414
+ <collected_sec>1723810534</collected_sec>
415
+ <collected_usec>87400</collected_usec>
416
+ <status>512</status>
417
+ <status_hint>0</status_hint>
418
+ <monitor>1</monitor>
419
+ <monitormode>0</monitormode>
420
+ <onreboot>0</onreboot>
421
+ <pendingaction>0</pendingaction>
422
+ </service>
423
+ <service type="4">
424
+ <name>hostOk</name>
425
+ <collected_sec>1723810534</collected_sec>
426
+ <collected_usec>89652</collected_usec>
427
+ <status>0</status>
428
+ <status_hint>0</status_hint>
429
+ <monitor>1</monitor>
430
+ <monitormode>0</monitormode>
431
+ <onreboot>0</onreboot>
432
+ <pendingaction>0</pendingaction>
433
+ <icmp>
434
+ <type>Ping</type>
435
+ <responsetime>0.000144</responsetime>
436
+ </icmp>
437
+ <port>
438
+ <hostname>10.20.4.200</hostname>
439
+ <portnumber>19999</portnumber>
440
+ <request><![CDATA[/api/v1/info]]></request>
441
+ <protocol>HTTP</protocol>
442
+ <type>TCP</type>
443
+ <responsetime>0.002077</responsetime>
444
+ </port>
445
+ </service>
446
+ <service type="4">
447
+ <name>hostDisabled</name>
448
+ <collected_sec>1723810534</collected_sec>
449
+ <collected_usec>69066</collected_usec>
450
+ <status>0</status>
451
+ <status_hint>0</status_hint>
452
+ <monitor>0</monitor>
453
+ <monitormode>0</monitormode>
454
+ <onreboot>0</onreboot>
455
+ <pendingaction>0</pendingaction>
456
+ </service>
457
+ <service type="4">
458
+ <name>hostAlert</name>
459
+ <collected_sec>1723810534</collected_sec>
460
+ <collected_usec>89857</collected_usec>
461
+ <status>32</status>
462
+ <status_hint>0</status_hint>
463
+ <monitor>1</monitor>
464
+ <monitormode>0</monitormode>
465
+ <onreboot>0</onreboot>
466
+ <pendingaction>0</pendingaction>
467
+ <icmp>
468
+ <type>Ping</type>
469
+ <responsetime>0.000069</responsetime>
470
+ </icmp>
471
+ <port>
472
+ <hostname>10.20.4.200</hostname>
473
+ <portnumber>19991</portnumber>
474
+ <request><![CDATA[/api/v1/info]]></request>
475
+ <protocol>HTTP</protocol>
476
+ <type>TCP</type>
477
+ <responsetime>-1.000000</responsetime>
478
+ </port>
479
+ </service>
480
+ <service type="4">
481
+ <name>hostNotExists</name>
482
+ <collected_sec>1723810549</collected_sec>
483
+ <collected_usec>94459</collected_usec>
484
+ <status>16384</status>
485
+ <status_hint>0</status_hint>
486
+ <monitor>1</monitor>
487
+ <monitormode>0</monitormode>
488
+ <onreboot>0</onreboot>
489
+ <pendingaction>0</pendingaction>
490
+ <icmp>
491
+ <type>Ping</type>
492
+ <responsetime>-1.000000</responsetime>
493
+ </icmp>
494
+ <port>
495
+ <hostname>10.20.4.233</hostname>
496
+ <portnumber>19999</portnumber>
497
+ <request><![CDATA[/api/v1/info]]></request>
498
+ <protocol>HTTP</protocol>
499
+ <type>TCP</type>
500
+ <responsetime>-1.000000</responsetime>
501
+ </port>
502
+ </service>
503
+ <service type="8">
504
+ <name>networkOk</name>
505
+ <collected_sec>1723810549</collected_sec>
506
+ <collected_usec>94801</collected_usec>
507
+ <status>0</status>
508
+ <status_hint>0</status_hint>
509
+ <monitor>1</monitor>
510
+ <monitormode>0</monitormode>
511
+ <onreboot>0</onreboot>
512
+ <pendingaction>0</pendingaction>
513
+ <link>
514
+ <state>1</state>
515
+ <speed>-1000000</speed>
516
+ <duplex>-1</duplex>
517
+ <download>
518
+ <packets>
519
+ <now>0</now>
520
+ <total>319258</total>
521
+ </packets>
522
+ <bytes>
523
+ <now>0</now>
524
+ <total>714558077</total>
525
+ </bytes>
526
+ <errors>
527
+ <now>0</now>
528
+ <total>0</total>
529
+ </errors>
530
+ </download>
531
+ <upload>
532
+ <packets>
533
+ <now>0</now>
534
+ <total>172909</total>
535
+ </packets>
536
+ <bytes>
537
+ <now>0</now>
538
+ <total>25128489</total>
539
+ </bytes>
540
+ <errors>
541
+ <now>0</now>
542
+ <total>0</total>
543
+ </errors>
544
+ </upload>
545
+ </link>
546
+ </service>
547
+ <service type="8">
548
+ <name>networkDisabled</name>
549
+ <collected_sec>1723810534</collected_sec>
550
+ <collected_usec>69103</collected_usec>
551
+ <status>0</status>
552
+ <status_hint>0</status_hint>
553
+ <monitor>0</monitor>
554
+ <monitormode>0</monitormode>
555
+ <onreboot>0</onreboot>
556
+ <pendingaction>0</pendingaction>
557
+ </service>
558
+ <service type="8">
559
+ <name>networkAlert</name>
560
+ <collected_sec>1723810549</collected_sec>
561
+ <collected_usec>94969</collected_usec>
562
+ <status>8388608</status>
563
+ <status_hint>0</status_hint>
564
+ <monitor>1</monitor>
565
+ <monitormode>0</monitormode>
566
+ <onreboot>0</onreboot>
567
+ <pendingaction>0</pendingaction>
568
+ <link>
569
+ <state>0</state>
570
+ <speed>-1</speed>
571
+ <duplex>-1</duplex>
572
+ <download>
573
+ <packets>
574
+ <now>-1</now>
575
+ <total>-1</total>
576
+ </packets>
577
+ <bytes>
578
+ <now>-1</now>
579
+ <total>-1</total>
580
+ </bytes>
581
+ <errors>
582
+ <now>-1</now>
583
+ <total>-1</total>
584
+ </errors>
585
+ </download>
586
+ <upload>
587
+ <packets>
588
+ <now>-1</now>
589
+ <total>-1</total>
590
+ </packets>
591
+ <bytes>
592
+ <now>-1</now>
593
+ <total>-1</total>
594
+ </bytes>
595
+ <errors>
596
+ <now>-1</now>
597
+ <total>-1</total>
598
+ </errors>
599
+ </upload>
600
+ </link>
601
+ </service>
602
+ <service type="8">
603
+ <name>networkNotExists</name>
604
+ <collected_sec>1723810549</collected_sec>
605
+ <collected_usec>94992</collected_usec>
606
+ <status>8388608</status>
607
+ <status_hint>0</status_hint>
608
+ <monitor>1</monitor>
609
+ <monitormode>0</monitormode>
610
+ <onreboot>0</onreboot>
611
+ <pendingaction>0</pendingaction>
612
+ <link>
613
+ <state>-1</state>
614
+ <speed>-1</speed>
615
+ <duplex>-1</duplex>
616
+ <download>
617
+ <packets>
618
+ <now>-1</now>
619
+ <total>-1</total>
620
+ </packets>
621
+ <bytes>
622
+ <now>-1</now>
623
+ <total>-1</total>
624
+ </bytes>
625
+ <errors>
626
+ <now>-1</now>
627
+ <total>-1</total>
628
+ </errors>
629
+ </download>
630
+ <upload>
631
+ <packets>
632
+ <now>-1</now>
633
+ <total>-1</total>
634
+ </packets>
635
+ <bytes>
636
+ <now>-1</now>
637
+ <total>-1</total>
638
+ </bytes>
639
+ <errors>
640
+ <now>-1</now>
641
+ <total>-1</total>
642
+ </errors>
643
+ </upload>
644
+ </link>
645
+ </service>
646
+ <service type="5">
647
+ <name>pve-deb-work</name>
648
+ <collected_sec>1723810549</collected_sec>
649
+ <collected_usec>94992</collected_usec>
650
+ <status>0</status>
651
+ <status_hint>0</status_hint>
652
+ <monitor>1</monitor>
653
+ <monitormode>0</monitormode>
654
+ <onreboot>0</onreboot>
655
+ <pendingaction>0</pendingaction>
656
+ <filedescriptors>
657
+ <allocated>1664</allocated>
658
+ <unused>0</unused>
659
+ <maximum>9223372036854775807</maximum>
660
+ </filedescriptors>
661
+ <system>
662
+ <load>
663
+ <avg01>0.00</avg01>
664
+ <avg05>0.04</avg05>
665
+ <avg15>0.03</avg15>
666
+ </load>
667
+ <cpu>
668
+ <user>0.0</user>
669
+ <system>0.0</system>
670
+ <nice>0.0</nice>
671
+ <wait>0.0</wait>
672
+ <hardirq>0.0</hardirq>
673
+ <softirq>0.0</softirq>
674
+ <steal>0.0</steal>
675
+ <guest>0.0</guest>
676
+ <guestnice>0.0</guestnice>
677
+ </cpu>
678
+ <memory>
679
+ <percent>3.1</percent>
680
+ <kilobyte>1020120</kilobyte>
681
+ </memory>
682
+ <swap>
683
+ <percent>0.0</percent>
684
+ <kilobyte>0</kilobyte>
685
+ </swap>
686
+ </system>
687
+ </service>
688
+</monit>