2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
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:
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
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
58 # Duplex: Unknown! (255)
59 # Auto-negotiation: on
61 # Address: de:ad:be:af:00:01
65 # Auto-negotiation: on
68 linux_nic_check
= "lnx_if"
71 def _parse_lnx_if_node_info(lines
):
73 return line
[0], line
[1:]
76 def _parse_lnx_if_ipaddress(lines
):
81 node
, line
= _parse_lnx_if_node_info(lines
)
85 if line
== ['[end_iplink]']:
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])))
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]
111 def _parse_lnx_if_sections(info
):
119 node
, line
= _parse_lnx_if_node_info(lines
)
120 except StopIteration:
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())})
134 elif line
[0].startswith('[') and line
[0].endswith(']'):
135 # Parse 'ethtool'; part two
141 iface
= ethtool_stats
.setdefault((node
, line
[0][1:-1]), {})
142 iface
.update({"index": index
})
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()
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
)
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
, {}))
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])
169 ifIndex
= attr
.get("index", index
)
173 # Guess type from name of interface
180 speed_text
= attr
.get("Speed")
181 if speed_text
is None:
184 if speed_text
== '65535Mb/s': # unknown
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
195 # Performance counters
196 ifInOctets
= counters
[0]
197 inucast
= counters
[1] + counters
[7]
198 inmcast
= counters
[7]
200 ifInDiscards
= counters
[3]
201 ifInErrors
= counters
[2]
202 ifOutOctets
= counters
[8]
203 outucast
= counters
[9]
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")
218 # No information from ethtool. We consider interfaces up
219 # if they have been used at least some time since the
221 if_state
= attr
.get('state')
224 ifOperStatus
= 1 # assume up
226 ifOperStatus
= 4 # unknown (NIC has never been used)
230 elif if_state
== "DOWN":
235 if attr
.get("Address"):
236 ifPhysAddress
= "".join([chr(int(x
, 16)) for x
in attr
.get("Address", "").split(":")])
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
248 return if_table
, ip_stats
251 def inventory_lnx_if(parsed
):
252 if linux_nic_check
== "legacy":
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'],
270 'has_perfdata': True,
272 'default_levels_variable': 'if_default_levels',