2023-04-21 23:54:20 -06:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import argparse
|
2023-06-25 22:40:36 -06:00
|
|
|
import statistics
|
2023-04-21 23:54:20 -06:00
|
|
|
import sys
|
|
|
|
import traceback
|
2023-04-21 23:54:20 -06:00
|
|
|
import warnings
|
2023-04-21 23:54:20 -06:00
|
|
|
|
2023-06-25 22:40:36 -06:00
|
|
|
import cf_speedtest
|
|
|
|
import cf_speedtest.options as options
|
|
|
|
import urllib3
|
|
|
|
from cf_speedtest.speedtest import run_standard_test
|
2023-04-21 23:54:20 -06:00
|
|
|
|
|
|
|
import checker.nagios as nagios
|
2023-06-15 11:00:41 -06:00
|
|
|
from checker import print_icinga2_check_status
|
2023-06-25 22:40:36 -06:00
|
|
|
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
|
|
|
|
}
|
2023-04-21 23:54:20 -06:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
"""
|
|
|
|
Copied from __main__.py
|
|
|
|
https://github.com/tevslin/cloudflarepycli/blob/main/src/cloudflarepycli/__main__.py
|
|
|
|
"""
|
|
|
|
parser = argparse.ArgumentParser(description='Check internet speed.')
|
2023-04-21 23:54:20 -06:00
|
|
|
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.')
|
2023-04-21 23:54:20 -06:00
|
|
|
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()
|
|
|
|
|
2023-04-21 23:54:20 -06:00
|
|
|
# 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)
|
2023-06-25 22:40:36 -06:00
|
|
|
speedtest_results = run_speedtest()
|
2023-04-21 23:54:20 -06:00
|
|
|
|
2023-06-27 13:13:52 -06:00
|
|
|
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')
|
2023-06-15 11:00:41 -06:00
|
|
|
exit_code = max(upload_speed_state, download_speed_state, latency_state)
|
2023-06-25 22:40:36 -06:00
|
|
|
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"
|
2023-04-21 23:54:20 -06:00
|
|
|
|
2023-06-15 11:00:41 -06:00
|
|
|
perfdata = {
|
|
|
|
'upload': {
|
2023-06-25 22:40:36 -06:00
|
|
|
'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),
|
2023-06-15 11:00:41 -06:00
|
|
|
'min': 0,
|
|
|
|
'unit': 'B'
|
|
|
|
},
|
|
|
|
'download': {
|
2023-06-25 22:40:36 -06:00
|
|
|
'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),
|
2023-06-15 11:00:41 -06:00
|
|
|
'min': 0,
|
|
|
|
'unit': 'B'
|
|
|
|
},
|
|
|
|
'latency_ms': {
|
2023-06-25 22:40:36 -06:00
|
|
|
'value': round(speedtest_results['latency'], 2),
|
|
|
|
'warn': round(args.warn_latency, 2),
|
|
|
|
'crit': round(args.critical_latency, 2),
|
2023-06-15 11:00:41 -06:00
|
|
|
'min': 0,
|
|
|
|
'unit': 'ms'
|
|
|
|
},
|
|
|
|
'jitter_ms': {
|
2023-06-25 22:40:36 -06:00
|
|
|
'value': round(speedtest_results['jitter'], 2),
|
2023-06-15 11:00:41 -06:00
|
|
|
'min': 0,
|
|
|
|
'unit': 'ms'
|
|
|
|
}
|
2023-04-21 23:54:20 -06:00
|
|
|
|
2023-06-15 11:00:41 -06:00
|
|
|
}
|
2023-04-21 23:54:20 -06:00
|
|
|
|
2023-06-15 11:00:41 -06:00
|
|
|
print_icinga2_check_status(text_result, exit_code, perfdata)
|
2023-04-21 23:54:20 -06:00
|
|
|
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)
|