#!/usr/bin/env python3 import argparse import statistics import sys import traceback import warnings import cf_speedtest import cf_speedtest.options as options import urllib3 from cf_speedtest.speedtest import run_standard_test import checker.nagios as nagios from checker import print_icinga2_check_status from checker.linuxfabric.base import get_state def run_speedtest(): # Set up the arguments for the speed test argv = ["--percentile", "90", "--testpatience", "15"] urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) parser = argparse.ArgumentParser() parser_with_args = options.add_run_options(parser) args = parser_with_args.parse_args(argv) percentile = args.percentile VERIFY_SSL = args.verifyssl OUTPUT_FILE = args.output patience = args.testpatience proxy = args.proxy # Taken from speed.cloudflare.com # byte count for each test, ranging from 100KB to 250MB measurement_sizes = \ [ 100_000, 1_000_000, 10_000_000, 25_000_000, 100_000_000, 250_000_000, ] def fake_print(*args, **kwargs): return normal_print = __builtins__.print # silence the speedtest __builtins__.print = fake_print speeds = run_standard_test(measurement_sizes, percentile, True, patience) __builtins__.print = normal_print # Extract the results return { 'download_speed': speeds['download_speed'] / 1_000_000, # Convert to Mbit/sec 'upload_speed': speeds['upload_speed'] / 1_000_000, # Convert to Mbit/sec 'percentile': f"{args.percentile}{'th percentile results:':<24}", 'latency': cf_speedtest.speedtest.percentile(speeds['latency_measurements'], 50), # Median latency in ms 'jitter': statistics.stdev(speeds['latency_measurements']), # Jitter in ms 'raw': speeds } def main(): """ Copied from __main__.py https://github.com/tevslin/cloudflarepycli/blob/main/src/cloudflarepycli/__main__.py """ parser = argparse.ArgumentParser(description='Check internet speed.') parser.add_argument('--warn-up', type=int, default=0, help='Warn level of upload speed in Mbps.') parser.add_argument('--critical-up', type=int, default=0, help='Critical level of upload speed in Mbps.') parser.add_argument('--warn-down', type=int, default=0, help='Warn level of download speed in Mbps.') parser.add_argument('--critical-down', type=int, default=0, help='Critical level of download speed in Mbps.') parser.add_argument('--warn-latency', type=int, default=60, help='Warn level of latency in ms.') parser.add_argument('--critical-latency', type=int, default=100, help='Critical level of latency in ms.') args = parser.parse_args() # Silence this error: """ lib/python3.10/site-packages/numpy/core/fromnumeric.py:3464: RuntimeWarning: Mean of empty slice. return _methods._mean(a, axis=axis, dtype=dtype, lib/python3.10/site-packages/numpy/core/_methods.py:192: RuntimeWarning: invalid value encountered in scalar divide ret = ret.dtype.type(ret / rcount) """ with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) speedtest_results = run_speedtest() upload_speed_state = get_state(speedtest_results['upload_speed'], args.warn_up, args.critical_up, operator='le') download_speed_state = get_state(speedtest_results['download_speed'], args.warn_down, args.critical_down, operator='le') latency_state = get_state(speedtest_results['latency'], args.warn_latency, args.critical_latency, operator='ge') exit_code = max(upload_speed_state, download_speed_state, latency_state) text_result = f"upload: {speedtest_results['upload_speed']:.1f} Mbps, download: {speedtest_results['download_speed']:.1f} Mbps, latency: {speedtest_results['latency']:.1f} ms, jitter: {speedtest_results['jitter']:.1f} ms" perfdata = { 'upload': { 'value': round(speedtest_results['upload_speed'] * 1e+6, 2), 'warn': round(args.warn_up * 1e+6, 2), 'crit': round(args.critical_up * 1e+6, 2), 'min': 0, 'unit': 'B' }, 'download': { 'value': round(speedtest_results['download_speed'] * 1e+6, 2), 'warn': round(args.warn_down * 1e+6, 2), 'crit': round(args.critical_down * 1e+6, 2), 'min': 0, 'unit': 'B' }, 'latency_ms': { 'value': round(speedtest_results['latency'], 2), 'warn': round(args.warn_latency, 2), 'crit': round(args.critical_latency, 2), 'min': 0, 'unit': 'ms' }, 'jitter_ms': { 'value': round(speedtest_results['jitter'], 2), 'min': 0, 'unit': 'ms' } } print_icinga2_check_status(text_result, exit_code, perfdata) sys.exit(exit_code) if __name__ == "__main__": try: main() except Exception as e: print(f'UNKNOWN: exception "{e}"') print(traceback.format_exc()) sys.exit(nagios.UNKNOWN)