Cleanup config.nodes_of
[check_mk.git] / checks / systemd_units
blob4def27fd6d58b4a07868e30991025d02be1f918b
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):
66 parsed = {}
68 if not info:
69 return {}
71 # second to last line (see above): "X loaded units listed."
72 count = int(info[-2][0])
73 if count == 0:
74 return parsed
76 UnitEntry = collections.namedtuple("UnitEntry",
77 ['name', 'type', 'load', 'active', 'sub', 'description'])
78 for row in info[1:count + 1]:
79 if row[0] == '*': # remove the '● ' (seems to get converted to '*')
80 row.pop(0)
81 line = ' '.join(row)
82 for unit_marker in _SYSTEMD_UNITS:
83 if unit_marker in line:
84 utype = unit_marker.strip('. ')
85 name, remains = line.split(unit_marker, 1)
86 load, active, sub, descr = remains.split(' ', 3)
87 unit = UnitEntry(name, utype, load, active, sub, descr)
88 parsed.setdefault(unit.type, {})[unit.name] = unit
89 break
91 return parsed
96 check_info['systemd_units'] = {
97 'parse_function': parse_systemd_units,
100 # .--services------------------------------------------------------------.
101 # | _ |
102 # | ___ ___ _ ____ _(_) ___ ___ ___ |
103 # | / __|/ _ \ '__\ \ / / |/ __/ _ \/ __| |
104 # | \__ \ __/ | \ V /| | (_| __/\__ \ |
105 # | |___/\___|_| \_/ |_|\___\___||___/ |
106 # | |
107 # '----------------------------------------------------------------------'
109 factory_settings["systemd_services_default_levels"] = {
110 "states": {
111 "active": 0,
112 "inactive": 0,
113 "failed": 2,
115 "states_default": 2,
116 "else": 2, # missleading name, used if service vanishes
119 discovery_systemd_units_services_rules = []
122 def discovery_systemd_units_services(parsed):
123 services = parsed.get('service', {})
125 def regex_match(what, name):
126 if not what:
127 return True
128 for entry in what:
129 if entry.startswith("~"):
130 if regex(entry[1:]).match(name):
131 return True
132 else:
133 continue
134 elif entry == name:
135 return True
136 return False
138 def state_match(rule_states, state):
139 return any(s in (None, state) for s in rule_states)
141 for rule in discovery_systemd_units_services_rules:
142 settings = rule[0]
143 descriptions = settings.get("descriptions", [])
144 names = settings.get("names", [])
145 states = settings.get("states")
146 for service in services.values():
147 if (regex_match(descriptions, service.description) and
148 regex_match(names, service.name) and state_match(states, service.active)):
149 yield service.name, {}
152 def check_systemd_units_services(item, params, parsed):
153 services = parsed.get('service', {})
154 service = services.get(item, None)
155 if service is None:
156 yield params["else"], "Service not found"
157 return
159 state = params["states"].get(service.active, params["states_default"])
160 yield state, "Status: %s" % service.active
161 yield 0, service.description
164 check_info['systemd_units.services'] = {
165 'inventory_function': discovery_systemd_units_services,
166 'check_function': check_systemd_units_services,
167 'service_description': 'Systemd Service %s',
168 'group': 'systemd_services',
169 'default_levels_variable': 'systemd_services_default_levels',
173 def discovery_systemd_units_services_summary(parsed):
174 if parsed:
175 yield 'Summary', {}
178 def check_systemd_units_services_summary(_no_item, params, parsed):
179 services = parsed.get('service', {}).values()
181 yield 0, "%d services in total" % len(services)
183 all_states = sorted(set(s.active for s in services))
185 for active_state in all_states:
186 state = params["states"].get(active_state, params["states_default"])
187 if state == 0:
188 continue
190 service_names = sorted(s.name for s in services if s.active == active_state)
191 cnt = len(service_names)
192 service_names = ", ".join(service_names)
193 yield state, "%d service%s %s (%s)" % (cnt, '' if cnt == 1 else 's', active_state,
194 service_names)
197 check_info['systemd_units.services_summary'] = {
198 'inventory_function': discovery_systemd_units_services_summary,
199 'check_function': check_systemd_units_services_summary,
200 'service_description': 'Systemd Service %s',
201 'group': 'systemd_services',
202 'default_levels_variable': 'systemd_services_default_levels',