#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (c) 2020 Binero AB https://binero.com # Copyright (c) 2013 Catalyst IT http://www.catalyst.net.nz # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import os import re import subprocess import sys import socket import json CEPH_COMMAND = '/usr/bin/ceph' STATUS_OK = 0 STATUS_CRITICAL = 2 STATUS_UNKNOWN = 3 def main(): parser = argparse.ArgumentParser(description="'ceph osd' nagios plugin.") parser.add_argument('-e','--exe', help='ceph executable [%s]' % CEPH_COMMAND) parser.add_argument('-c','--conf', help='alternative ceph conf file') parser.add_argument('-m','--monaddress', help='ceph monitor address[:port]') parser.add_argument('-i','--id', help='ceph client id') parser.add_argument('-k','--keyring', help='ceph client keyring file') parser.add_argument('-H','--host', help='osd host', required=True) parser.add_argument('-C','--critical', help='critical threshold', default=60) args = parser.parse_args() ceph_exec = args.exe if args.exe else CEPH_COMMAND if not os.path.exists(ceph_exec): print("UNKNOWN: ceph executable '%s' doesn't exist" % ceph_exec) return STATUS_UNKNOWN if args.conf and not os.path.exists(args.conf): print("UNKNOWN: ceph conf file '%s' doesn't exist" % args.conf) return STATUS_UNKNOWN if args.keyring and not os.path.exists(args.keyring): print("UNKNOWN: keyring file '%s' doesn't exist" % args.keyring) return STATUS_UNKNOWN if not args.host: print("UNKNOWN: no OSD hostname given") return STATUS_UNKNOWN try: addrinfo = socket.getaddrinfo(args.host, None, 0, socket.SOCK_STREAM) args.host = addrinfo[0][-1][0] if addrinfo[0][0] == socket.AF_INET6: args.host = "[%s]" % args.host except Exception: print('UNKNOWN: could not resolve %s' % args.host) return STATUS_UNKNOWN ceph_cmd = [ceph_exec] if args.monaddress: ceph_cmd.append('-m') ceph_cmd.append(args.monaddress) if args.conf: ceph_cmd.append('-c') ceph_cmd.append(args.conf) if args.id: ceph_cmd.append('--id') ceph_cmd.append(args.id) if args.keyring: ceph_cmd.append('--keyring') ceph_cmd.append(args.keyring) ceph_cmd.append('osd') ceph_cmd.append('dump') p = subprocess.Popen(ceph_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) output, err = p.communicate() if err or not output: print("CRITICAL: %s" % err) return STATUS_CRITICAL # escape IPv4 host address osd_host = args.host.replace('.', '\.') # escape IPv6 host address osd_host = osd_host.replace('[', '\[') osd_host = osd_host.replace(']', '\]') osds_up = re.findall(r"^(osd\.[^ ]*) up.*%s:" % (osd_host), output, re.MULTILINE) final_status = STATUS_OK lines = [] for osd in osds_up: daemon_ceph_cmd = [ceph_exec, '--format', 'json'] daemon_ceph_cmd.append('daemon') daemon_ceph_cmd.append(osd) daemon_ceph_cmd.append('perf') daemon_ceph_cmd.append('dump') p = subprocess.Popen(daemon_ceph_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) output, err = p.communicate() if err or not output: print("CRITICAL: %s" % err) return STATUS_CRITICAL try: data = json.loads(output) except Exception: print("CRITICAL: failed to load json") return STATUS_CRITICAL bluefs = data.get('bluefs', None) if not bluefs: continue db_total_bytes = bluefs.get('db_total_bytes') db_used_bytes = bluefs.get('db_used_bytes') perc = (float(db_used_bytes) / float(db_total_bytes) * 100) if perc >= args.critical and final_status == STATUS_OK: final_status = STATUS_CRITICAL lines.append("%s=%.2f%%" % (osd, perc)) if final_status == STATUS_OK: print("OK: %s" % (' '.join(lines))) else: print("CRITICAL: %s" % (' '.join(lines))) return final_status if __name__ == "__main__": sys.exit(main())