Refactoring: Changed all check parameters starting with an 'o' to the new rulespec...
[check_mk.git] / checks / systemd_units
blob8a9bf105dd88778a62bad4f564162e5bf19f6904
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2018 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
27 # <<<systemd_units>>>
28 # UNIT LOAD ACTIVE SUB DESCRIPTION
29 # ● check-mk-enterprise-2018.07.24.service loaded failed failed LSB: OMD sites
30 # ● systemd-cryptsetup@cryptswap1.service loaded failed failed Cryptography Setup for cryptswap1
31 # ● swapfile.swap loaded failed failed /swapfile
33 # LOAD = Reflects whether the unit definition was properly loaded.
34 # ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
35 # SUB = The low-level unit activation state, values depend on unit type.
37 # 3 loaded units listed. Pass --all to see loaded but inactive units, too.
38 # To show all installed unit files use 'systemctl list-unit-files'.
40 # .--Parse function------------------------------------------------------.
41 # | ____ __ _ _ |
42 # | | _ \ __ _ _ __ ___ ___ / _|_ _ _ __ ___| |_(_) ___ _ __ |
43 # | | |_) / _` | '__/ __|/ _ \ | |_| | | | '_ \ / __| __| |/ _ \| '_ \ |
44 # | | __/ (_| | | \__ \ __/ | _| |_| | | | | (__| |_| | (_) | | | | |
45 # | |_| \__,_|_| |___/\___| |_| \__,_|_| |_|\___|\__|_|\___/|_| |_| |
46 # | |
47 # '----------------------------------------------------------------------'
49 _SYSTEMD_UNITS = [
50 '.service ', # A service unit describes how to manage a service or application on the server. This will include how to start or stop the service, under which circumstances it should be automatically started, and the dependency and ordering information for related software.
51 '.socket ', # A socket unit file describes a network or IPC socket, or a FIFO buffer that systemd uses for socket-based activation. These always have an associated .service file that will be started when activity is seen on the socket that this unit defines.
52 '.device ', # A unit that describes a device that has been designated as needing systemd management by udev or the sysfs filesystem. Not all devices will have .device files. Some scenarios where .device units may be necessary are for ordering, mounting, and accessing the devices.
53 '.mount ', # This unit defines a mountpoint on the system to be managed by systemd. These are named after the mount path, with slashes changed to dashes. Entries within /etc/fstab can have units created automatically.
54 '.automount ', # An .automount unit configures a mountpoint that will be automatically mounted. These must be named after the mount point they refer to and must have a matching .mount unit to define the specifics of the mount.
55 '.swap ', # This unit describes swap space on the system. The name of these units must reflect the device or file path of the space.
56 '.target ', # A target unit is used to provide synchronization points for other units when booting up or changing states. They also can be used to bring the system to a new state. Other units specify their relation to targets to become tied to the target's operations.
57 '.path ', # This unit defines a path that can be used for path-based activation. By default, a .service unit of the same base name will be started when the path reaches the specified state. This uses inotify to monitor the path for changes.
58 '.timer ', # A .timer unit defines a timer that will be managed by systemd, similar to a cron job for delayed or scheduled activation. A matching unit will be started when the timer is reached.
59 '.snapshot ', # A .snapshot unit is created automatically by the systemctl snapshot command. It allows you to reconstruct the current state of the system after making changes. Snapshots do not survive across sessions and are used to roll back temporary states.
60 '.slice ', # A .slice unit is associated with Linux Control Group nodes, allowing resources to be restricted or assigned to any processes associated with the slice. The name reflects its hierarchical position within the cgroup tree. Units are placed in certain slices by default depending on their type.
61 '.scope ', # Scope units are created automatically by systemd from information received from its bus interfaces. These are used to manage sets of system processes that are created externally.
65 def parse_systemd_units(info):
67 parsed = {}
68 # second to last line (see above): "X loaded units listed."
69 count = int(info[-2][0])
70 if count == 0:
71 return parsed
73 UnitEntry = collections.namedtuple("UnitEntry",
74 ['name', 'type', 'load', 'active', 'sub', 'description'])
75 for row in info[1:count + 1]:
76 if row[0] == '*': # remove the '● ' (seems to get converted to '*')
77 row.pop(0)
78 line = ' '.join(row)
79 for unit_marker in _SYSTEMD_UNITS:
80 if unit_marker in line:
81 utype = unit_marker.strip('. ')
82 name, remains = line.split(unit_marker, 1)
83 load, active, sub, descr = remains.split(' ', 3)
84 unit = UnitEntry(name, utype, load, active, sub, descr)
85 parsed.setdefault(unit.type, {})[unit.name] = unit
86 break
88 return parsed
93 check_info['systemd_units'] = {
94 'parse_function': parse_systemd_units,
97 # .--services------------------------------------------------------------.
98 # | _ |
99 # | ___ ___ _ ____ _(_) ___ ___ ___ |
100 # | / __|/ _ \ '__\ \ / / |/ __/ _ \/ __| |
101 # | \__ \ __/ | \ V /| | (_| __/\__ \ |
102 # | |___/\___|_| \_/ |_|\___\___||___/ |
103 # | |
104 # '----------------------------------------------------------------------'
106 factory_settings["systemd_services_default_levels"] = {
107 "states": {
108 "active": 0,
109 "inactive": 0,
110 "failed": 2,
112 "states_default": 2,
113 "else": 2, # missleading name, used if service vanishes
116 discovery_systemd_units_services_rules = []
119 def discovery_systemd_units_services(parsed):
121 services = parsed.get('service', {})
123 def regex_match(what, name):
124 if not what:
125 return True
126 for entry in what:
127 if entry.startswith("~"):
128 if regex(entry[1:]).match(name):
129 return True
130 else:
131 continue
132 elif entry == name:
133 return True
134 return False
136 def state_match(rule_states, state):
137 return any(s in (None, state) for s in rule_states)
139 for rule in discovery_systemd_units_services_rules:
140 settings = rule[0]
141 descriptions = settings.get("descriptions", [])
142 names = settings.get("names", [])
143 states = settings.get("states")
144 for service in services.values():
145 if (regex_match(descriptions, service.description) and
146 regex_match(names, service.name) and state_match(states, service.active)):
147 yield service.name, {}
150 def check_systemd_units_services(item, params, parsed):
152 services = parsed.get('service', {})
153 service = services.get(item, None)
154 if service is None:
155 yield params["else"], "Service not found"
156 return
158 state = params["states"].get(service.active, params["states_default"])
159 yield state, "Status: %s" % service.active
160 yield 0, service.description
163 check_info['systemd_units.services'] = {
164 'inventory_function': discovery_systemd_units_services,
165 'check_function': check_systemd_units_services,
166 'service_description': 'Systemd Service %s',
167 'group': 'systemd_services',
168 'default_levels_variable': 'systemd_services_default_levels',
172 def discovery_systemd_units_services_summary(_no_parsed):
173 yield 'Summary', {}
176 def check_systemd_units_services_summary(_no_item, params, parsed):
177 services = parsed.get('service', {}).values()
179 yield 0, "%d services in total" % len(services)
181 all_states = sorted(set(s.active for s in services))
183 for active_state in all_states:
184 state = params["states"].get(active_state, params["states_default"])
185 if state == 0:
186 continue
188 service_names = sorted(s.name for s in services if s.active == active_state)
189 cnt = len(service_names)
190 service_names = ", ".join(service_names)
191 yield state, "%d service%s %s (%s)" % (cnt, '' if cnt == 1 else 's', active_state,
192 service_names)
195 check_info['systemd_units.services_summary'] = {
196 'inventory_function': discovery_systemd_units_services_summary,
197 'check_function': check_systemd_units_services_summary,
198 'service_description': 'Systemd Service %s',
199 'group': 'systemd_services',
200 'default_levels_variable': 'systemd_services_default_levels',