Cleanup config.nodes_of
[check_mk.git] / checks / lnx_if
blob284ee2b65d3a24782a4c6ebb4da378f7c6553bb8
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2014 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 # Example output from agent:
29 # <<<lnx_if>>>
30 # [start_iplink]
31 # 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
32 # link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
33 # inet 127.0.0.1/8 scope host lo
34 # valid_lft forever preferred_lft forever
35 # inet6 ::1/128 scope host
36 # valid_lft forever preferred_lft forever
37 # 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
38 # link/ether 00:27:13:b4:a9:ec brd ff:ff:ff:ff:ff:ff
39 # inet 127.0.0.1/8 scope host lo
40 # valid_lft forever preferred_lft forever
41 # inet6 ::1/128 scope host
42 # valid_lft forever preferred_lft forever
43 # 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
44 # link/ether 00:21:6a:10:8e:b8 brd ff:ff:ff:ff:ff:ff
45 # inet 127.0.0.1/8 scope host lo
46 # valid_lft forever preferred_lft forever
47 # inet6 ::1/128 scope host
48 # valid_lft forever preferred_lft forever
49 # [end_iplink]
50 # <<<lnx_if:sep(58)>>>
51 # lo: 4520 54 0 0 0 0 0 0 4520 54 0 0 0 0 0 0
52 # eth0: 0 0 0 0 0 0 0 0 1710 5 0 0 0 0 0 0
53 # eth1: 78505 555 0 0 0 0 0 0 132569 523 0 0 0 0 0 0
54 # [lo]
55 # Link detected: yes
56 # [eth0]
57 # Speed: 65535Mb/s
58 # Duplex: Unknown! (255)
59 # Auto-negotiation: on
60 # Link detected: no
61 # Address: de:ad:be:af:00:01
62 # [eth1]
63 # Speed: 1000Mb/s
64 # Duplex: Full
65 # Auto-negotiation: on
66 # Link detected: yes
68 linux_nic_check = "lnx_if"
71 def _parse_lnx_if_node_info(lines):
72 line = lines.next()
73 return line[0], line[1:]
76 def _parse_lnx_if_ipaddress(lines):
77 ip_stats = {}
78 iface = None
79 while True:
80 try:
81 node, line = _parse_lnx_if_node_info(lines)
82 except StopIteration:
83 break
85 if line == ['[end_iplink]']:
86 break
88 if line[0].endswith(":") and line[1].endswith(":"):
89 # Some (docker) interfaces have suffix "@..." but ethtool does not show this suffix.
90 # 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default ...
91 # 5: veth6a06585@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue ...
92 iface = ip_stats.setdefault((node, line[1][:-1].split("@")[0]), {})
93 iface.update(dict(zip(line[3::2], line[4::2])))
94 continue
96 if not iface:
97 continue
99 if line[0].startswith('link/'):
100 # link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
101 iface[line[0]] = line[1]
102 iface[line[2]] = line[3]
104 elif line[0].startswith('inet'):
105 # inet 127.0.0.1/8 scope host lo
106 # inet6 ::1/128 scope host
107 iface[line[0]] = line[1]
108 return ip_stats
111 def _parse_lnx_if_sections(info):
112 ip_stats = {}
113 ethtool_stats = {}
114 iface = None
115 lines = iter(info)
116 index = 0
117 while True:
118 try:
119 node, line = _parse_lnx_if_node_info(lines)
120 except StopIteration:
121 break
123 if line[0].startswith("[start_iplink]"):
124 # Parse 'ip link/address' section (as fallback in case ethtool is missing)
125 ip_stats.update(_parse_lnx_if_ipaddress(lines))
127 elif len(line) == 2 and len(line[1].split()) >= 16:
128 # Parse 'ethtool'; part one
129 # IFACE_NAME: VAL VAL VAL ...
130 iface = ethtool_stats.setdefault((node, line[0]), {})
131 iface.update({"counters": map(int, line[1].split())})
132 continue
134 elif line[0].startswith('[') and line[0].endswith(']'):
135 # Parse 'ethtool'; part two
136 # [IF_NAME]
137 # KEY: VAL
138 # KEY: VAL
139 # ...
140 index += 1
141 iface = ethtool_stats.setdefault((node, line[0][1:-1]), {})
142 iface.update({"index": index})
143 continue
145 if iface is not None:
146 stripped_line0 = line[0].strip()
147 if stripped_line0 == "Address":
148 iface[stripped_line0] = ":".join(line[1:]).strip()
149 else:
150 iface[stripped_line0] = " ".join(line[1:]).strip()
152 return ip_stats, ethtool_stats
155 def parse_lnx_if(info):
156 ip_stats, ethtool_stats = _parse_lnx_if_sections(info)
158 nic_info = {}
159 for k in set(ip_stats.keys()).union(ethtool_stats.keys()):
160 iface = ip_stats.get(k, {})
161 iface.update(ethtool_stats.get(k, {}))
162 nic_info[k] = iface
164 if_table = []
165 index = 0
166 for (node_info, nic), attr in nic_info.iteritems():
167 counters = attr.get("counters", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
168 index += 1
169 ifIndex = attr.get("index", index)
170 ifDescr = nic
171 ifAlias = nic
173 # Guess type from name of interface
174 if nic == "lo":
175 ifType = 24
176 else:
177 ifType = 6
179 # Compute speed
180 speed_text = attr.get("Speed")
181 if speed_text is None:
182 ifSpeed = ''
183 else:
184 if speed_text == '65535Mb/s': # unknown
185 ifSpeed = ''
186 elif speed_text.endswith("Kb/s"):
187 ifSpeed = int(speed_text[:-4]) * 1000
188 elif speed_text.endswith("Mb/s"):
189 ifSpeed = int(speed_text[:-4]) * 1000000
190 elif speed_text.endswith("Gb/s"):
191 ifSpeed = int(speed_text[:-4]) * 1000000000
192 else:
193 ifSpeed = ''
195 # Performance counters
196 ifInOctets = counters[0]
197 inucast = counters[1] + counters[7]
198 inmcast = counters[7]
199 inbcast = 0
200 ifInDiscards = counters[3]
201 ifInErrors = counters[2]
202 ifOutOctets = counters[8]
203 outucast = counters[9]
204 outmcast = 0
205 outbcast = 0
206 ifOutDiscards = counters[11]
207 ifOutErrors = counters[10]
208 ifOutQLen = counters[12]
210 # Link state from ethtool. If ethtool has no information about
211 # this NIC, we set the state to unknown.
212 ld = attr.get("Link detected")
213 if ld == "yes":
214 ifOperStatus = 1
215 elif ld == "no":
216 ifOperStatus = 2
217 else:
218 # No information from ethtool. We consider interfaces up
219 # if they have been used at least some time since the
220 # system boot.
221 if_state = attr.get('state')
222 if if_state is None:
223 if ifInOctets > 0:
224 ifOperStatus = 1 # assume up
225 else:
226 ifOperStatus = 4 # unknown (NIC has never been used)
227 else:
228 if if_state == "UP":
229 ifOperStatus = 1
230 elif if_state == "DOWN":
231 ifOperStatus = 2
232 else:
233 ifOperStatus = 4
235 if attr.get("Address"):
236 ifPhysAddress = "".join([chr(int(x, 16)) for x in attr.get("Address", "").split(":")])
237 else:
238 ifPhysAddress = ''
240 row = [node_info] + map(str, [
241 ifIndex, ifDescr, ifType, ifSpeed, ifOperStatus, ifInOctets, inucast, inmcast, inbcast,
242 ifInDiscards, ifInErrors, ifOutOctets, outucast, outmcast, outbcast, ifOutDiscards,
243 ifOutErrors, ifOutQLen, ifAlias, ifPhysAddress
246 if_table.append(row)
248 return if_table, ip_stats
251 def inventory_lnx_if(parsed):
252 if linux_nic_check == "legacy":
253 return []
254 # Always exclude dockers veth* interfaces on docker nodes
255 if_table = [e for e in parsed[0] if not e[2].startswith("veth")]
256 return inventory_if_common(if_table, has_nodeinfo=True)
259 def check_lnx_if(item, params, parsed):
260 return check_if_common(item, params, parsed[0], has_nodeinfo=True)
263 check_info["lnx_if"] = {
264 'parse_function': parse_lnx_if,
265 'inventory_function': inventory_lnx_if,
266 'check_function': check_lnx_if,
267 'service_description': 'Interface %s',
268 'includes': ['if.include'],
269 'node_info': True,
270 'has_perfdata': True,
271 'group': 'if',
272 'default_levels_variable': 'if_default_levels',