Skip to content

Instantly share code, notes, and snippets.

@SpotlightKid
Last active March 11, 2016 18:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SpotlightKid/aae3293f1a30ee21f04c to your computer and use it in GitHub Desktop.
Save SpotlightKid/aae3293f1a30ee21f04c to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ntppeerstats.py
#
# Copyright 2016 Christopher Arndt <chris- at- chrisarndt -dot- de>
#
"""Get NTP daemon peer statistics and graph time offsets.
Documentation of the file format:
http://doc.ntp.org/current-stable/monopt.html
Documentation about the Modified Julian Date used there:
http://tycho.usno.navy.mil/mjd.html
To prepare the peerstats files as written out by the NTP server
and and left by logrotation to one big input file for this script:
$ rsync -rv --update example.com:/var/log/ntpstats/ .
$ zcat peerstats.*.gz | cat - peerstats.???????? peerstats | \
sort -n > peerstats-all
"""
from __future__ import print_function, unicode_literals
import argparse
import logging
import sys
import time
from collections import namedtuple
from datetime import datetime, timedelta
import pandas as pd
from matplotlib import pyplot as pl
# https://gist.github.com/jiffyclub/1294443
from jdutil import mjd_to_jd, jd_to_date
log = logging.getLogger('ntppeerstats')
Peerstat = namedtuple("Peerstat",
"timestamp day seconds address status offset delay dispersion skew")
types = (int, float, str, lambda x: int(x, 16), float, float, float, float)
def mjd_seconds_to_dt(mjd, seconds):
year, month, day = jd_to_date(mjd_to_jd(mjd))
return (datetime(year, month, int(day)) + timedelta(seconds=seconds))
def read_peerstats(filename):
with open(filename) as fp:
for lineno, line in enumerate(fp):
values = [f(x) for f, x in zip(types, line.strip().split())]
try:
timestamp = mjd_seconds_to_dt(values[0], values[1])
except:
log.exception("Could not parse date on line %i: day=%i, "
"seconds=%f", lineno, values[0], values[1])
timestamp = None
stat = Peerstat(*[timestamp] + values)
yield stat
def main(args=None):
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument('-v', '--verbose', action="store_true",
help="Be verbose")
ap.add_argument('peerstats', default="/var/log/ntpstats/peerstats",
help="Path of peerstats file")
args = ap.parse_args(args if args is not None else sys.argv[1:])
logging.basicConfig(format="%(levelname)s: %(message)s",
level=logging.DEBUG if args.verbose else logging.INFO)
s = time.time()
peerstats = read_peerstats(args.peerstats)
ps = pd.DataFrame.from_records(peerstats, columns=Peerstat._fields)
d = time.time() - s
log.debug("Read peerstats in %0.4f sec." % d)
#ps.info()
#print()
print(ps[['offset', 'delay', 'dispersion', 'skew']].describe())
ax = pl.gca()
for _, group in ps.groupby('address'):
group.plot(x='timestamp', y='offset', ax=ax)
pl.show()
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]) or 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment