check_systemd 2.1.0__tar.gz → 2.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: check_systemd
3
- Version: 2.1.0
3
+ Version: 2.2.1
4
4
  Summary: Nagios / Icinga monitoring plugin to check systemd for failed units.
5
5
  Home-page: https://github.com/Josef-Friedrich/check_systemd
6
6
  Author: Josef Friedrich
@@ -33,7 +33,8 @@ Description: [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https
33
33
  ## Command line interface
34
34
 
35
35
  ```
36
- usage: check_systemd [-h] [-c SECONDS] [-e UNIT | -u UNIT] [-v] [-V] [-w SECONDS]
36
+ usage: check_systemd [-h] [-u UNIT | -e UNIT] [-n] [-w SECONDS] [-c SECONDS]
37
+ [-t] [-W SECONDS] [-C SECONDS] [-v] [-V]
37
38
 
38
39
  Copyright (c) 2014-18 Andrea Briganti a.k.a 'Kbyte' <kbytesys@gmail.com>
39
40
  Copyright (c) 2019-20 Josef Friedrich <josef@friedrich.rocks>
@@ -42,17 +43,43 @@ Description: [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https
42
43
 
43
44
  optional arguments:
44
45
  -h, --help show this help message and exit
45
- -c SECONDS, --critical SECONDS
46
- Startup time in seconds to result in critical status.
47
- -e UNIT, --exclude UNIT
48
- Exclude a systemd unit from the checks. This option can be applied multiple times, for example: -e mnt-data.mount -e
49
- task.service. Regular expressions can be used to exclude multiple units at once, for example: -e 'user@\d+\.service'. For
50
- more informations see the Python documentation about regular expressions (https://docs.python.org/3/library/re.html).
51
46
  -u UNIT, --unit UNIT Name of the systemd unit that is being tested.
47
+ -e UNIT, --exclude UNIT
48
+ Exclude a systemd unit from the checks. This option can
49
+ be applied multiple times, for example: -e mnt-
50
+ data.mount -e task.service. Regular expressions can be
51
+ used to exclude multiple units at once, for example: -e
52
+ 'user@\d+\.service'. For more informations see the
53
+ Python documentation about regular expressions
54
+ (https://docs.python.org/3/library/re.html).
55
+ -n, --no-startup-time
56
+ Don’t check the startup time. Using this option the
57
+ options '-w, --warning' and '-c, --critical' have no
58
+ effect. Performance data about the startup time is
59
+ collected, but no critical, warning etc. states are
60
+ triggered.
61
+ -w SECONDS, --warning SECONDS
62
+ Startup time in seconds to result in a warning status.
63
+ Thedefault is 60 seconds.
64
+ -c SECONDS, --critical SECONDS
65
+ Startup time in seconds to result in a critical status.
66
+ Thedefault is 120 seconds.
67
+ -t, --dead-timers Detect dead / inactive timers. See the corresponding
68
+ options '-W, --dead-timer-warning' and '-C, --dead-
69
+ timers-critical'. Dead timers are detected by parsing
70
+ the output of 'systemctl list-timers'. Dead timer rows
71
+ displaying 'n/a' in the NEXT and LEFTcolumns and the
72
+ time span in the column PASSED exceeds the values
73
+ specified with the options '-W, --dead-timer-warning'
74
+ and '-C, --dead-timers-critical'.
75
+ -W SECONDS, --dead-timers-warning SECONDS
76
+ Time ago in seconds for dead / inactive timers to
77
+ trigger a warning state (by default 6 days).
78
+ -C SECONDS, --dead-timers-critical SECONDS
79
+ Time ago in seconds for dead / inactive timers to
80
+ trigger a critical state (by default 7 days).
52
81
  -v, --verbose Increase output verbosity (use up to 3 times).
53
82
  -V, --version show program's version number and exit
54
- -w SECONDS, --warning SECONDS
55
- Startup time in seconds to result in warning status.
56
83
 
57
84
  Performance data:
58
85
  - count_units
@@ -70,12 +97,38 @@ Description: [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https
70
97
  * https://exchange.icinga.com/joseffriedrich/check_systemd
71
98
  * https://exchange.nagios.org/directory/Plugins/System-Metrics/Processes/check_systemd/details
72
99
 
100
+ ## Behind the scenes
101
+
102
+ To detect failed units this monitoring script runs:
103
+
104
+ ```sh
105
+ systemctl list-units --all
106
+ ```
107
+
108
+ To get the startup time it executes:
109
+
110
+ ```sh
111
+ systemd-analyze
112
+ ```
113
+
114
+ To check a specific unit (`-u, --unit`) this command is executed:
115
+
116
+ ```sh
117
+ systemctl is-active <unit-name>
118
+ ```
119
+
120
+ To find dead timers this plugin launches:
121
+
122
+ ```sh
123
+ systemctl list-timers --all
124
+ ```
125
+
73
126
  ## Testing
74
127
 
75
128
  ```
76
- pyenv install 3.7.6
77
- pyenv install 3.8.1
78
- pyenv local 3.7.6 3.8.1
129
+ pyenv install 3.6.12
130
+ pyenv install 3.7.9
131
+ pyenv local 3.6.12 3.7.9
79
132
  pip3 install tox
80
133
  tox
81
134
  ```
@@ -0,0 +1,135 @@
1
+ [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https://pypi.python.org/pypi/check_systemd)
2
+ [![Build Status](https://travis-ci.org/Josef-Friedrich/check_systemd.svg?branch=master)](https://travis-ci.org/Josef-Friedrich/check_systemd)
3
+
4
+ # check_systemd
5
+
6
+ `check_systemd` is a
7
+ [Nagios](https://www.nagios.org) / [Icinga](https://icinga.com)
8
+ monitoring plugin to check [systemd](https://systemd.io) for failed
9
+ units.
10
+
11
+ This Python script will report a degraded system to your monitoring solution.
12
+ It requires only the
13
+ [nagiosplugin](https://nagiosplugin.readthedocs.io/en/stable) library.
14
+
15
+ You can also test a single service with the `-u, --unit` parameter.
16
+
17
+ Released under GNU GPLv2 License.
18
+
19
+ ## Installation
20
+
21
+ ```
22
+ pip3 install check_systemd
23
+ ```
24
+
25
+ ## Command line interface
26
+
27
+ ```
28
+ usage: check_systemd [-h] [-u UNIT | -e UNIT] [-n] [-w SECONDS] [-c SECONDS]
29
+ [-t] [-W SECONDS] [-C SECONDS] [-v] [-V]
30
+
31
+ Copyright (c) 2014-18 Andrea Briganti a.k.a 'Kbyte' <kbytesys@gmail.com>
32
+ Copyright (c) 2019-20 Josef Friedrich <josef@friedrich.rocks>
33
+
34
+ Nagios / Icinga monitoring plugin to check systemd for failed units.
35
+
36
+ optional arguments:
37
+ -h, --help show this help message and exit
38
+ -u UNIT, --unit UNIT Name of the systemd unit that is being tested.
39
+ -e UNIT, --exclude UNIT
40
+ Exclude a systemd unit from the checks. This option can
41
+ be applied multiple times, for example: -e mnt-
42
+ data.mount -e task.service. Regular expressions can be
43
+ used to exclude multiple units at once, for example: -e
44
+ 'user@\d+\.service'. For more informations see the
45
+ Python documentation about regular expressions
46
+ (https://docs.python.org/3/library/re.html).
47
+ -n, --no-startup-time
48
+ Don’t check the startup time. Using this option the
49
+ options '-w, --warning' and '-c, --critical' have no
50
+ effect. Performance data about the startup time is
51
+ collected, but no critical, warning etc. states are
52
+ triggered.
53
+ -w SECONDS, --warning SECONDS
54
+ Startup time in seconds to result in a warning status.
55
+ Thedefault is 60 seconds.
56
+ -c SECONDS, --critical SECONDS
57
+ Startup time in seconds to result in a critical status.
58
+ Thedefault is 120 seconds.
59
+ -t, --dead-timers Detect dead / inactive timers. See the corresponding
60
+ options '-W, --dead-timer-warning' and '-C, --dead-
61
+ timers-critical'. Dead timers are detected by parsing
62
+ the output of 'systemctl list-timers'. Dead timer rows
63
+ displaying 'n/a' in the NEXT and LEFTcolumns and the
64
+ time span in the column PASSED exceeds the values
65
+ specified with the options '-W, --dead-timer-warning'
66
+ and '-C, --dead-timers-critical'.
67
+ -W SECONDS, --dead-timers-warning SECONDS
68
+ Time ago in seconds for dead / inactive timers to
69
+ trigger a warning state (by default 6 days).
70
+ -C SECONDS, --dead-timers-critical SECONDS
71
+ Time ago in seconds for dead / inactive timers to
72
+ trigger a critical state (by default 7 days).
73
+ -v, --verbose Increase output verbosity (use up to 3 times).
74
+ -V, --version show program's version number and exit
75
+
76
+ Performance data:
77
+ - count_units
78
+ - startup_time
79
+ - units_activating
80
+ - units_active
81
+ - units_failed
82
+ - units_inactive
83
+
84
+ ```
85
+
86
+ ## Project pages
87
+
88
+ * https://github.com/Josef-Friedrich/check_systemd
89
+ * https://exchange.icinga.com/joseffriedrich/check_systemd
90
+ * https://exchange.nagios.org/directory/Plugins/System-Metrics/Processes/check_systemd/details
91
+
92
+ ## Behind the scenes
93
+
94
+ To detect failed units this monitoring script runs:
95
+
96
+ ```sh
97
+ systemctl list-units --all
98
+ ```
99
+
100
+ To get the startup time it executes:
101
+
102
+ ```sh
103
+ systemd-analyze
104
+ ```
105
+
106
+ To check a specific unit (`-u, --unit`) this command is executed:
107
+
108
+ ```sh
109
+ systemctl is-active <unit-name>
110
+ ```
111
+
112
+ To find dead timers this plugin launches:
113
+
114
+ ```sh
115
+ systemctl list-timers --all
116
+ ```
117
+
118
+ ## Testing
119
+
120
+ ```
121
+ pyenv install 3.6.12
122
+ pyenv install 3.7.9
123
+ pyenv local 3.6.12 3.7.9
124
+ pip3 install tox
125
+ tox
126
+ ```
127
+
128
+ ## Deploying
129
+
130
+ Edit version number in check_systemd.py (without `v`)
131
+
132
+ ```
133
+ git tag v2.0.11
134
+ git push --tags
135
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: check-systemd
3
- Version: 2.1.0
3
+ Version: 2.2.1
4
4
  Summary: Nagios / Icinga monitoring plugin to check systemd for failed units.
5
5
  Home-page: https://github.com/Josef-Friedrich/check_systemd
6
6
  Author: Josef Friedrich
@@ -33,7 +33,8 @@ Description: [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https
33
33
  ## Command line interface
34
34
 
35
35
  ```
36
- usage: check_systemd [-h] [-c SECONDS] [-e UNIT | -u UNIT] [-v] [-V] [-w SECONDS]
36
+ usage: check_systemd [-h] [-u UNIT | -e UNIT] [-n] [-w SECONDS] [-c SECONDS]
37
+ [-t] [-W SECONDS] [-C SECONDS] [-v] [-V]
37
38
 
38
39
  Copyright (c) 2014-18 Andrea Briganti a.k.a 'Kbyte' <kbytesys@gmail.com>
39
40
  Copyright (c) 2019-20 Josef Friedrich <josef@friedrich.rocks>
@@ -42,17 +43,43 @@ Description: [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https
42
43
 
43
44
  optional arguments:
44
45
  -h, --help show this help message and exit
45
- -c SECONDS, --critical SECONDS
46
- Startup time in seconds to result in critical status.
47
- -e UNIT, --exclude UNIT
48
- Exclude a systemd unit from the checks. This option can be applied multiple times, for example: -e mnt-data.mount -e
49
- task.service. Regular expressions can be used to exclude multiple units at once, for example: -e 'user@\d+\.service'. For
50
- more informations see the Python documentation about regular expressions (https://docs.python.org/3/library/re.html).
51
46
  -u UNIT, --unit UNIT Name of the systemd unit that is being tested.
47
+ -e UNIT, --exclude UNIT
48
+ Exclude a systemd unit from the checks. This option can
49
+ be applied multiple times, for example: -e mnt-
50
+ data.mount -e task.service. Regular expressions can be
51
+ used to exclude multiple units at once, for example: -e
52
+ 'user@\d+\.service'. For more informations see the
53
+ Python documentation about regular expressions
54
+ (https://docs.python.org/3/library/re.html).
55
+ -n, --no-startup-time
56
+ Don’t check the startup time. Using this option the
57
+ options '-w, --warning' and '-c, --critical' have no
58
+ effect. Performance data about the startup time is
59
+ collected, but no critical, warning etc. states are
60
+ triggered.
61
+ -w SECONDS, --warning SECONDS
62
+ Startup time in seconds to result in a warning status.
63
+ Thedefault is 60 seconds.
64
+ -c SECONDS, --critical SECONDS
65
+ Startup time in seconds to result in a critical status.
66
+ Thedefault is 120 seconds.
67
+ -t, --dead-timers Detect dead / inactive timers. See the corresponding
68
+ options '-W, --dead-timer-warning' and '-C, --dead-
69
+ timers-critical'. Dead timers are detected by parsing
70
+ the output of 'systemctl list-timers'. Dead timer rows
71
+ displaying 'n/a' in the NEXT and LEFTcolumns and the
72
+ time span in the column PASSED exceeds the values
73
+ specified with the options '-W, --dead-timer-warning'
74
+ and '-C, --dead-timers-critical'.
75
+ -W SECONDS, --dead-timers-warning SECONDS
76
+ Time ago in seconds for dead / inactive timers to
77
+ trigger a warning state (by default 6 days).
78
+ -C SECONDS, --dead-timers-critical SECONDS
79
+ Time ago in seconds for dead / inactive timers to
80
+ trigger a critical state (by default 7 days).
52
81
  -v, --verbose Increase output verbosity (use up to 3 times).
53
82
  -V, --version show program's version number and exit
54
- -w SECONDS, --warning SECONDS
55
- Startup time in seconds to result in warning status.
56
83
 
57
84
  Performance data:
58
85
  - count_units
@@ -70,12 +97,38 @@ Description: [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https
70
97
  * https://exchange.icinga.com/joseffriedrich/check_systemd
71
98
  * https://exchange.nagios.org/directory/Plugins/System-Metrics/Processes/check_systemd/details
72
99
 
100
+ ## Behind the scenes
101
+
102
+ To detect failed units this monitoring script runs:
103
+
104
+ ```sh
105
+ systemctl list-units --all
106
+ ```
107
+
108
+ To get the startup time it executes:
109
+
110
+ ```sh
111
+ systemd-analyze
112
+ ```
113
+
114
+ To check a specific unit (`-u, --unit`) this command is executed:
115
+
116
+ ```sh
117
+ systemctl is-active <unit-name>
118
+ ```
119
+
120
+ To find dead timers this plugin launches:
121
+
122
+ ```sh
123
+ systemctl list-timers --all
124
+ ```
125
+
73
126
  ## Testing
74
127
 
75
128
  ```
76
- pyenv install 3.7.6
77
- pyenv install 3.8.1
78
- pyenv local 3.7.6 3.8.1
129
+ pyenv install 3.6.12
130
+ pyenv install 3.7.9
131
+ pyenv local 3.6.12 3.7.9
79
132
  pip3 install tox
80
133
  tox
81
134
  ```
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
2
+
3
3
  """
4
4
  You can redistribute it and/or modify it under the terms of the GNU General
5
5
  Public License as published by the Free Software Foundation, either version 2
@@ -14,10 +14,10 @@ import re
14
14
  import nagiosplugin
15
15
  from nagiosplugin import Metric
16
16
 
17
- __version__ = '2.1.0'
17
+ __version__ = '2.2.1'
18
18
 
19
19
 
20
- class SystemdctlListUnitsResource(nagiosplugin.Resource):
20
+ class SystemctlListUnitsResource(nagiosplugin.Resource):
21
21
  """
22
22
  :param list excludes: A list of systemd unit names.
23
23
  """
@@ -41,8 +41,8 @@ class SystemdctlListUnitsResource(nagiosplugin.Resource):
41
41
  # We don’t use `systemctl --failed --no-legend`, because we want to
42
42
  # collect performance data of all units.
43
43
  try:
44
- p = subprocess.Popen(['systemctl', 'list-units', '--all',
45
- '--no-legend'],
44
+ # p = subprocess.Popen(['./test/bin/ok/systemctl', 'list-units', '--all'], # noqa: E501
45
+ p = subprocess.Popen(['systemctl', 'list-units', '--all'],
46
46
  stderr=subprocess.PIPE,
47
47
  stdin=subprocess.PIPE,
48
48
  stdout=subprocess.PIPE)
@@ -61,16 +61,32 @@ class SystemdctlListUnitsResource(nagiosplugin.Resource):
61
61
  'inactive': [],
62
62
  }
63
63
  if stdout:
64
- # Output of `systemctl list-units --all --no-legend`:
64
+ lines = stdout.decode('utf-8').splitlines()
65
+ table_heading = lines[0]
66
+
67
+ # Remove the first line because it is the header.
68
+
69
+ # Remove the last seven lines:
70
+
71
+ # empty line
72
+ # LOAD = Reflects whether the unit definition...
73
+ # ACTIVE = The high-level unit activation state...
74
+ # SUB = The low-level unit activation state...
75
+ # empty line
76
+ # xxx loaded units listed. Pass --all to see ...
77
+ # To show all installed unit files use...
78
+ table_body = lines[1:-7]
79
+ table_parser = TableParser(table_heading)
80
+ # Output of `systemctl list-units --all:
65
81
  # UNIT LOAD ACTIVE SUB JOB DESCRIPTION
66
82
  # foobar.service loaded active waiting Description text
67
83
  count_units = 0
68
- for line in io.StringIO(stdout.decode('utf-8')):
69
- split_line = line.split()
84
+ for line in table_body:
70
85
  # foobar.service
71
- unit = split_line[0]
86
+ unit = table_parser.get_column_text(line, 'UNIT')
72
87
  # failed
73
- active = split_line[2]
88
+ active = table_parser.get_column_text(line, 'ACTIVE')
89
+
74
90
  # Only count not excluded units.
75
91
  if not self.re_match(unit):
76
92
  # Quick fix:
@@ -102,17 +118,29 @@ class SystemdctlListUnitsResource(nagiosplugin.Resource):
102
118
  def format_timespan_to_seconds(fmt_timespan):
103
119
  """Convert a timespan format string into secondes.
104
120
 
105
- :param str fmt_timespan: for example `2.345s` or `3min 45.234s`
121
+ https://github.com/systemd/systemd/blob/master/src/basic/time-util.c#L357
122
+
123
+ :param str fmt_timespan: for example `2.345s` or `3min 45.234s` or
124
+ `34min left` or `2 months 8 days`
106
125
 
107
126
  :return: The seconds
108
127
  :rtype: float
109
128
  """
129
+ for replacement in [
130
+ ['years', 'y'],
131
+ ['months', 'month'],
132
+ ['weeks', 'w'],
133
+ ['days', 'd'],
134
+ ]:
135
+ fmt_timespan = fmt_timespan.replace(
136
+ ' ' + replacement[0], replacement[1]
137
+ )
110
138
  seconds = {
111
- 'y': 365 * 24 * 60 * 60,
112
- 'month': 30 * 24 * 60 * 60,
113
- 'w': 7 * 24 * 60 * 60,
114
- 'd': 24 * 60 * 60,
115
- 'h': 60 * 60,
139
+ 'y': 31536000, # 365 * 24 * 60 * 60
140
+ 'month': 2592000, # 30 * 24 * 60 * 60
141
+ 'w': 604800, # 7 * 24 * 60 * 60
142
+ 'd': 86400, # 24 * 60 * 60
143
+ 'h': 3600, # 60 * 60
116
144
  'min': 60,
117
145
  's': 1,
118
146
  'ms': 0.001,
@@ -120,9 +148,10 @@ def format_timespan_to_seconds(fmt_timespan):
120
148
  result = 0
121
149
  for span in fmt_timespan.split():
122
150
  match = re.search(r'([\d\.]+)([a-z]+)', span)
123
- value = match.group(1)
124
- unit = match.group(2)
125
- result += float(value) * seconds[unit]
151
+ if match:
152
+ value = match.group(1)
153
+ unit = match.group(2)
154
+ result += float(value) * seconds[unit]
126
155
  return round(float(result), 3)
127
156
 
128
157
 
@@ -170,6 +199,142 @@ class SystemdAnalyseResource(nagiosplugin.Resource):
170
199
  context='startup_time')
171
200
 
172
201
 
202
+ class TableParser:
203
+ """A parser for the various table outputs of different systemd commands."""
204
+
205
+ def __init__(self, heading_row):
206
+ """
207
+ :param str heading_row: A row with column titles.
208
+ """
209
+ self.heading_row = heading_row
210
+
211
+ def detect_column_boundaries(self, column_title):
212
+ """
213
+ :param str column_title: The title of the column, for example UNIT,
214
+ ACTIVE. The column title must be included in the heading row.
215
+ """
216
+ match = re.search(re.compile(column_title + r'\s*'), self.heading_row)
217
+ return [match.start(), match.end()]
218
+
219
+ def get_column_text(self, row, column_title):
220
+ """Get the text of a certain column, that is specified by the column
221
+ title. Leading and trailing whitespaces are removed.
222
+
223
+ :param str row: The current row of the table to extract a certain
224
+ column.
225
+ :param str column_title: The title of the column, for example UNIT,
226
+ ACTIVE. The column title must be included in the heading row.
227
+ """
228
+ boundaries = self.detect_column_boundaries(column_title)
229
+ column = row[boundaries[0]:boundaries[1]]
230
+ return column.strip()
231
+
232
+
233
+ class SystemctlListTimersResource(nagiosplugin.Resource):
234
+ """There is one type of systemd "degradation" which is normally not
235
+ detected: dead / inactive timers.
236
+ """
237
+
238
+ def __init__(self, excludes=[], *args, **kwargs):
239
+ self.excludes = excludes
240
+ self.warning = kwargs.pop('warning')
241
+ self.critical = kwargs.pop('critical')
242
+ super().__init__(*args, **kwargs)
243
+
244
+ name = 'SYSTEMD'
245
+
246
+ column_names = [
247
+ 'NEXT', 'LEFT', 'LAST', 'PASSED', 'UNIT', 'ACTIVATES'
248
+ ]
249
+
250
+ column_boundaries = None
251
+
252
+ def re_match(self, unit):
253
+ for exclude in self.excludes:
254
+ if re.match(exclude, unit):
255
+ return(True)
256
+ return(False)
257
+
258
+ def detect_column_boundaries(self, heading):
259
+ boundaries = []
260
+ previous_column_start = 0
261
+ for column_title in self.column_names[1:]:
262
+ next_column_start = heading.index(column_title)
263
+ boundaries.append([previous_column_start, next_column_start])
264
+ previous_column_start = next_column_start
265
+ return boundaries
266
+
267
+ def get_column_text(self, row, column_name):
268
+ boundaries = self.column_boundaries[
269
+ self.column_names.index(column_name)
270
+ ]
271
+ return row[boundaries[0]:boundaries[1]].strip()
272
+
273
+ def probe(self):
274
+ """
275
+ :return: generator that emits
276
+ :class:`~nagiosplugin.metric.Metric` objects
277
+ """
278
+ try:
279
+ p = subprocess.Popen(['systemctl', 'list-timers', '--all'],
280
+ stderr=subprocess.PIPE,
281
+ stdin=subprocess.PIPE,
282
+ stdout=subprocess.PIPE)
283
+ stdout, stderr = p.communicate()
284
+ except OSError as e:
285
+ raise nagiosplugin.CheckError(e)
286
+
287
+ if stderr:
288
+ raise nagiosplugin.CheckError(stderr)
289
+
290
+ # NEXT LEFT
291
+ # Sat 2020-05-16 15:11:15 CEST 34min left
292
+
293
+ # LAST PASSED
294
+ # Sat 2020-05-16 14:31:56 CEST 4min 20s ago
295
+
296
+ # UNIT ACTIVATES
297
+ # apt-daily.timer apt-daily.service
298
+ if stdout:
299
+ lines = stdout.decode('utf-8').splitlines()
300
+ table_heading = lines[0]
301
+ self.column_boundaries = self.detect_column_boundaries(
302
+ table_heading
303
+ )
304
+ # Remove the first line because it is the header.
305
+ # Remove the two last lines: empty line + "XX timers listed."
306
+ table_body = lines[1:-2]
307
+
308
+ state = nagiosplugin.Ok # ok
309
+
310
+ for row in table_body:
311
+ unit = self.get_column_text(row, 'UNIT')
312
+ if self.re_match(unit):
313
+ continue
314
+ next_date_time = self.get_column_text(row, 'NEXT')
315
+
316
+ if next_date_time == 'n/a':
317
+ passed_text = self.get_column_text(row, 'PASSED')
318
+
319
+ if passed_text == 'n/a':
320
+ state = nagiosplugin.Critical
321
+ else:
322
+ passed = format_timespan_to_seconds(
323
+ passed_text
324
+ )
325
+
326
+ if passed_text == 'n/a' or passed >= self.critical:
327
+ state = nagiosplugin.Critical
328
+ elif passed >= self.warning:
329
+ state = nagiosplugin.Warn
330
+
331
+ yield Metric(
332
+ name=unit,
333
+ value=state,
334
+ context='dead_timers'
335
+ )
336
+
337
+
173
338
  class SystemctlIsActiveResource(nagiosplugin.Resource):
174
339
 
175
340
  name = 'SYSTEMD'
@@ -218,8 +383,10 @@ class UnitContext(nagiosplugin.Context):
218
383
  (may optionally be consulted)
219
384
  :returns: :class:`~.result.Result`
220
385
  """
221
- hint = '%s: %s' % (metric.name, metric.value) \
222
- if metric.value else metric.name
386
+ if metric.value:
387
+ hint = '{}: {}'.format(metric.name, metric.value)
388
+ else:
389
+ hint = metric.name
223
390
  if metric.value and metric.value != 'active':
224
391
  return self.result_cls(nagiosplugin.Critical, metric=metric,
225
392
  hint=hint)
@@ -227,6 +394,22 @@ class UnitContext(nagiosplugin.Context):
227
394
  return self.result_cls(nagiosplugin.Ok, metric=metric, hint=hint)
228
395
 
229
396
 
397
+ class DeadTimersContext(nagiosplugin.Context):
398
+
399
+ def __init__(self):
400
+ super(DeadTimersContext, self).__init__('dead_timers')
401
+
402
+ def evaluate(self, metric, resource):
403
+ """Determines state of a given metric.
404
+
405
+ :param metric: associated metric that is to be evaluated
406
+ :param resource: resource that produced the associated metric
407
+ (may optionally be consulted)
408
+ :returns: :class:`~.result.Result`
409
+ """
410
+ return self.result_cls(metric.value, metric=metric, hint=metric.name)
411
+
412
+
230
413
  class PerformanceDataContext(nagiosplugin.Context):
231
414
 
232
415
  def __init__(self):
@@ -266,7 +449,7 @@ class SystemdSummary(nagiosplugin.Summary):
266
449
  """
267
450
  summary = []
268
451
  for result in results.most_significant:
269
- if result.context.name in ['startup_time', 'unit']:
452
+ if result.context.name in ['startup_time', 'unit', 'dead_timers']:
270
453
  summary.append(result)
271
454
  return ', '.join(['{0}'.format(result) for result in summary])
272
455
 
@@ -278,14 +461,15 @@ class SystemdSummary(nagiosplugin.Summary):
278
461
  """
279
462
  summary = []
280
463
  for result in results.most_significant:
281
- if result.context.name in ['startup_time', 'unit']:
464
+ if result.context.name in ['startup_time', 'unit', 'dead_timers']:
282
465
  summary.append('{0}: {1}'.format(result.state, result))
283
466
  return summary
284
467
 
285
468
 
286
469
  def get_argparser():
287
470
  parser = argparse.ArgumentParser(
288
- formatter_class=argparse.RawDescriptionHelpFormatter,
471
+ prog='check_systemd', # To get the right command name in the README.
472
+ formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, width=80), # noqa: E501
289
473
  description= # noqa: E251
290
474
  'Copyright (c) 2014-18 Andrea Briganti a.k.a \'Kbyte\' <kbytesys@gmail.com>\n' # noqa: E501
291
475
  'Copyright (c) 2019-20 Josef Friedrich <josef@friedrich.rocks>\n'
@@ -303,11 +487,11 @@ def get_argparser():
303
487
 
304
488
  exclusive_group = parser.add_mutually_exclusive_group()
305
489
 
306
- parser.add_argument(
307
- '-c', '--critical',
308
- metavar='SECONDS',
309
- default=120,
310
- help='Startup time in seconds to result in critical status.',
490
+ exclusive_group.add_argument(
491
+ '-u', '--unit',
492
+ type=str,
493
+ dest='unit',
494
+ help='Name of the systemd unit that is being tested.',
311
495
  )
312
496
 
313
497
  exclusive_group.add_argument(
@@ -325,11 +509,62 @@ def get_argparser():
325
509
  '(https://docs.python.org/3/library/re.html).',
326
510
  )
327
511
 
328
- exclusive_group.add_argument(
329
- '-u', '--unit',
330
- type=str,
331
- dest='unit',
332
- help='Name of the systemd unit that is being tested.',
512
+ parser.add_argument(
513
+ '-n', '--no-startup-time',
514
+ action='store_true',
515
+ default=False,
516
+ help='Don’t check the startup time. Using this option the options '
517
+ '\'-w, --warning\' and \'-c, --critical\' have no effect. '
518
+ 'Performance data about the startup time is collected, but '
519
+ 'no critical, warning etc. states are triggered.',
520
+ )
521
+
522
+ parser.add_argument(
523
+ '-w', '--warning',
524
+ default=60,
525
+ metavar='SECONDS',
526
+ help='Startup time in seconds to result in a warning status. The'
527
+ 'default is 60 seconds.',
528
+ )
529
+
530
+ parser.add_argument(
531
+ '-c', '--critical',
532
+ metavar='SECONDS',
533
+ default=120,
534
+ help='Startup time in seconds to result in a critical status. The'
535
+ 'default is 120 seconds.',
536
+ )
537
+
538
+ parser.add_argument(
539
+ '-t', '--dead-timers',
540
+ action='store_true',
541
+ help='Detect dead / inactive timers. See the corresponding options '
542
+ '\'-W, --dead-timer-warning\' and '
543
+ '\'-C, --dead-timers-critical\'. '
544
+ 'Dead timers are detected by parsing the output of '
545
+ '\'systemctl list-timers\'. '
546
+ 'Dead timer rows displaying \'n/a\' in the NEXT and LEFT'
547
+ 'columns and the time span in the column PASSED exceeds the '
548
+ 'values specified with the options \'-W, --dead-timer-warning\' '
549
+ 'and \'-C, --dead-timers-critical\'.'
550
+ )
551
+
552
+ parser.add_argument(
553
+ '-W', '--dead-timers-warning',
554
+ metavar='SECONDS',
555
+ type=float,
556
+ default=60 * 60 * 24 * 6,
557
+ help='Time ago in seconds for dead / inactive timers to trigger a '
558
+ 'warning state (by default 6 days).'
559
+ )
560
+
561
+ parser.add_argument(
562
+ '-C', '--dead-timers-critical',
563
+ metavar='SECONDS',
564
+ type=float,
565
+ default=60 * 60 * 24 * 7,
566
+ help='Time ago in seconds for dead / inactive timers to trigger a '
567
+ 'critical state (by default 7 days).'
333
568
  )
334
569
 
335
570
  parser.add_argument(
@@ -345,13 +580,6 @@ def get_argparser():
345
580
  version='%(prog)s {}'.format(__version__),
346
581
  )
347
582
 
348
- parser.add_argument(
349
- '-w', '--warning',
350
- default=60,
351
- metavar='SECONDS',
352
- help='Startup time in seconds to result in warning status.',
353
- )
354
-
355
583
  return parser
356
584
 
357
585
 
@@ -360,30 +588,53 @@ def main():
360
588
 
361
589
  objects = []
362
590
 
591
+ if args.dead_timers:
592
+ objects += [
593
+ SystemctlListTimersResource(
594
+ excludes=args.exclude,
595
+ warning=args.dead_timers_warning,
596
+ critical=args.dead_timers_critical,
597
+ ),
598
+ DeadTimersContext()
599
+ ]
600
+
363
601
  if args.unit:
364
602
  objects.append(SystemctlIsActiveResource(unit=args.unit))
365
603
  else:
366
604
  objects += [
367
- SystemdctlListUnitsResource(excludes=args.exclude),
605
+ SystemctlListUnitsResource(excludes=args.exclude),
368
606
  PerformanceDataContext(),
369
-
370
607
  ]
371
- analyse = subprocess.run(['systemd-analyze'], stderr=subprocess.PIPE,
372
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
373
- # systemd-analyze: boot not finshed exists with 1
608
+ analyse = subprocess.run(
609
+ ['systemd-analyze'],
610
+ stderr=subprocess.PIPE,
611
+ stdin=subprocess.PIPE,
612
+ stdout=subprocess.PIPE
613
+ )
614
+ # systemd-analyze: boot not finshed exits with 1
374
615
  if analyse.returncode == 0:
375
616
  objects.append(SystemdAnalyseResource())
376
617
 
377
618
  objects += [
378
619
  UnitContext(),
379
- nagiosplugin.ScalarContext(
380
- name='startup_time',
381
- warning=args.warning,
382
- critical=args.critical,
383
- ),
384
620
  SystemdSummary()
385
621
  ]
386
622
 
623
+ if not args.no_startup_time:
624
+ objects.append(
625
+ nagiosplugin.ScalarContext(
626
+ name='startup_time',
627
+ warning=args.warning,
628
+ critical=args.critical,
629
+ )
630
+ )
631
+ else:
632
+ objects.append(
633
+ nagiosplugin.ScalarContext(
634
+ name='startup_time'
635
+ )
636
+ )
637
+
387
638
  check = nagiosplugin.Check(*objects)
388
639
  check.main(args.verbose)
389
640
 
@@ -1,82 +0,0 @@
1
- [![pypi.org](http://img.shields.io/pypi/v/check_systemd.svg)](https://pypi.python.org/pypi/check_systemd)
2
- [![Build Status](https://travis-ci.org/Josef-Friedrich/check_systemd.svg?branch=master)](https://travis-ci.org/Josef-Friedrich/check_systemd)
3
-
4
- # check_systemd
5
-
6
- `check_systemd` is a
7
- [Nagios](https://www.nagios.org) / [Icinga](https://icinga.com)
8
- monitoring plugin to check [systemd](https://systemd.io) for failed
9
- units.
10
-
11
- This Python script will report a degraded system to your monitoring solution.
12
- It requires only the
13
- [nagiosplugin](https://nagiosplugin.readthedocs.io/en/stable) library.
14
-
15
- You can also test a single service with the `-u, --unit` parameter.
16
-
17
- Released under GNU GPLv2 License.
18
-
19
- ## Installation
20
-
21
- ```
22
- pip3 install check_systemd
23
- ```
24
-
25
- ## Command line interface
26
-
27
- ```
28
- usage: check_systemd [-h] [-c SECONDS] [-e UNIT | -u UNIT] [-v] [-V] [-w SECONDS]
29
-
30
- Copyright (c) 2014-18 Andrea Briganti a.k.a 'Kbyte' <kbytesys@gmail.com>
31
- Copyright (c) 2019-20 Josef Friedrich <josef@friedrich.rocks>
32
-
33
- Nagios / Icinga monitoring plugin to check systemd for failed units.
34
-
35
- optional arguments:
36
- -h, --help show this help message and exit
37
- -c SECONDS, --critical SECONDS
38
- Startup time in seconds to result in critical status.
39
- -e UNIT, --exclude UNIT
40
- Exclude a systemd unit from the checks. This option can be applied multiple times, for example: -e mnt-data.mount -e
41
- task.service. Regular expressions can be used to exclude multiple units at once, for example: -e 'user@\d+\.service'. For
42
- more informations see the Python documentation about regular expressions (https://docs.python.org/3/library/re.html).
43
- -u UNIT, --unit UNIT Name of the systemd unit that is being tested.
44
- -v, --verbose Increase output verbosity (use up to 3 times).
45
- -V, --version show program's version number and exit
46
- -w SECONDS, --warning SECONDS
47
- Startup time in seconds to result in warning status.
48
-
49
- Performance data:
50
- - count_units
51
- - startup_time
52
- - units_activating
53
- - units_active
54
- - units_failed
55
- - units_inactive
56
-
57
- ```
58
-
59
- ## Project pages
60
-
61
- * https://github.com/Josef-Friedrich/check_systemd
62
- * https://exchange.icinga.com/joseffriedrich/check_systemd
63
- * https://exchange.nagios.org/directory/Plugins/System-Metrics/Processes/check_systemd/details
64
-
65
- ## Testing
66
-
67
- ```
68
- pyenv install 3.7.6
69
- pyenv install 3.8.1
70
- pyenv local 3.7.6 3.8.1
71
- pip3 install tox
72
- tox
73
- ```
74
-
75
- ## Deploying
76
-
77
- Edit version number in check_systemd.py (without `v`)
78
-
79
- ```
80
- git tag v2.0.11
81
- git push --tags
82
- ```
File without changes
File without changes
File without changes
File without changes