[Git][NTPsec/ntpsec][master] 2 commits: In pybtpq, improve implementation of showhostnames.
Eric S. Raymond
gitlab at mg.gitlab.com
Thu Oct 27 19:15:12 UTC 2016
Eric S. Raymond pushed to branch master at NTPsec / ntpsec
Commits:
87cd3df4 by Eric S. Raymond at 2016-10-27T13:40:00-04:00
In pybtpq, improve implementation of showhostnames.
- - - - -
5f0de4cd by Eric S. Raymond at 2016-10-27T15:13:40-04:00
Refactoring step - make peer summary generator reusable.
- - - - -
2 changed files:
- ntpq/pyntpq
- pylib/util.py
Changes:
=====================================
ntpq/pyntpq
=====================================
--- a/ntpq/pyntpq
+++ b/ntpq/pyntpq
@@ -130,7 +130,6 @@ NTP_2BIT = 0x9 # leap bits
class Ntpq(cmd.Cmd):
"ntpq command interpreter"
-
def __init__(self, session):
cmd.Cmd.__init__(self)
self.session = session
@@ -150,25 +149,6 @@ class Ntpq(cmd.Cmd):
self.debug = 0
self.pktversion = NTP_OLDVERSION + 1
self.uservars = collections.OrderedDict()
- # By default, the peer spreadsheet layout is designed so lines just
- # fit in 80 characters. This tells us how much extra horizontal space
- # we have available on a wider terminal emulator
- self.horizontal_slack = termsize()[1] - 80
- # Peer spreadsheet column widths
- self.namewidth = 15 + self.horizontal_slack
- self.refidwidth = 15
- # Compute peer spreadsheet headers
- self.__remote = " remote ".ljust(self.namewidth)
- self.__common = "st t when poll reach delay offset "
- self.__opeerheader = self.__remote + \
- " local ".ljust(self.refidwidth) + \
- self.__common + " disp\n"
- self.__peerheader = self.__remote + \
- " refid ".ljust(self.refidwidth) + \
- self.__common + "jitter\n"
- self.__apeerheader = self.__remote + \
- " refid assid ".ljust(self.refidwidth) + \
- self.__common + "jitter\n"
def emptyline(self):
"Called when an empty line is entered in response to the prompt."
@@ -188,15 +168,6 @@ usage: help [ command ]
# Unexposed helper tables and functions begin here
- @staticmethod
- def high_truncate(hostname, maxlen):
- "Truncate on the left using leading _ to indicate 'more'."
- # Used for local IPv6 addresses, best distinguished by low bits
- if len(hostname) <= maxlen:
- return hostname
- else:
- return '-' + hostname[-maxlen+1:]
-
def __dogetassoc(self):
try:
self.peers = self.session.readstat()
@@ -295,179 +266,23 @@ usage: help [ command ]
condition, last_event, event_count)
self.say(display + "\n")
- @staticmethod
- def prettyinterval(diff):
- "Print an interval in natural time units."
- if diff <= 0:
- return "-"
- if diff <= 2048:
- return str(diff)
- diff = (diff + 29) / 60
- if diff <= 300:
- return "%dm" % diff
- diff = (diff + 29) / 60
- if diff <= 96:
- return "%dh" % diff
- diff = (diff + 11) / 24
- return "%dd" % diff
-
- def __doprintpeers(self, variables, header, associd):
- hmode = 0
- srchost = None
- srcport = 0
- srcaddr = None
- dstadr_refid = ""
- ppoll = 0
- hpoll = 0
- reach = 0
- ptype = '?'
- have_jitter = False
- clock_name = ''
-
- now = time.time()
-
- for (name, value) in variables.items():
- if name in ("srcadr", "peeradr"):
- srcaddr = value
- elif name == "srchost":
- srchost = value
- elif name == "dstadr":
- # The C code tried to get a fallback pytpe from this in case
- # the hmode field was not included
- if "local" in header:
- dstadr_refid = value
- elif name == "hmode":
- hmode = value
- elif name == "refid":
- # The C code for this looked crazily overelaborate. Best
- # guess is that it was designed to deal with formats that
- # no longer occur in this field.
- if "refid" in header:
- dstadr_refid = value
- elif name == "hpoll":
- hpoll = value
- if hpoll < 0:
- hpoll = NTP_MINPOLL
- elif name == "ppoll":
- ppoll = value
- if ppoll < 0:
- ppoll = NTP_MINPOLL
- elif name == "reach":
- # Shipped as hex, displayed in octal
- reach = value
- elif name == "delay":
- estdelay = value
- elif name == "offset":
- estoffset = value
- elif name == "jitter":
- if "jitter" in header:
- estjitter = value
- have_jitter = True
- elif name == "rootdisp" or name == "dispersion":
- estdisp = value
- elif name == "rec":
- rec = value # l_fp timestamp
- elif name == "srcport" or name == "peerport":
- srcport = value
- elif name == "reftime":
- reftime = value # l_fp timestamp
- if hmode == MODE_BCLIENT:
- # broadcastclient or multicastclient
- ptype = 'b'
- elif hmode == MODE_BROADCAST:
- # broadcast or multicast server
- if srcaddr.startswith("224."): # IANA multicast address prefix
- ptype = 'M'
- else:
- ptype = 'B'
- elif hmode == MODE_CLIENT:
- if srchost and '(' in srchost:
- ptype = 'l' # local refclock
- elif dstadr_refid == "POOL":
- ptype = 'p' # pool
- elif srcaddr.startswith("224."):
- ptype = 'a' # manycastclient
- else:
- ptype = 'u' # unicast
- elif hmode == MODE_ACTIVE:
- ptype = 's' # symmetric active
- elif hmode == MODE_PASSIVE:
- ptype = 'S' # symmetric passive
-
- #
- # Got everything, format the line
- #
- poll_sec = 1 << min(ppoll, hpoll)
- if self.pktversion > NTP_OLDVERSION:
- c = " x.-+#*o"[CTL_PEER_STATVAL(self.session.rstatus) & 0x7]
- else:
- c = " .+*"[CTL_PEER_STATVAL(self.session.rstatus) & 0x3]
- if len(self.chosts) > 1:
- maxhostlen = max([len(host) for (host, _af) in self.chosts])
- self.say(Ntpq.high_truncate(self.session.hostname, maxhostlen)+ " ")
- # Source host or clockname
- if srchost != None:
- clock_name = srchost
- else:
- clock_name = canonicalize_dns(srcaddr)
- if interpreter.wideremote and len(clock_name) > self.namewidth:
- self.say("%c%s\n" % (c, clock_name))
- sys.stdout(" " * (self.namewidth + 2))
- else:
- self.say("%c%-*.*s " % \
- (c, self.namewidth, self.namewidth, clock_name[:self.namewidth]))
- # Destination address, assoc ID or refid.
- assocwidth = 7 if "assid" in header else 0
- if "." not in dstadr_refid:
- dstadr_refid = "." + dstadr_refid + "."
- if assocwidth and len(dstadr_refid) >= self.refidwidth - assocwidth:
- visible = "..."
- else:
- visible = dstadr_refid
- self.say(visible)
- if "assid" in header:
- self.say(" " * (self.refidwidth - len(visible) - assocwidth + 1))
- self.say("%-6d" % (associd))
- else:
- self.say(" " * (self.refidwidth - len(visible)))
- # The rest of the story
- last_sync = variables.get("rec") or variables.get("reftime")
- jd = estjitter if have_jitter else estdisp
- jd = " -" if jd >= 999 else ("%7.3f" % jd)
- self.say(
- " %2ld %c %4.4s %4.4s %3lo %7.3f %8.3f %s\n" % \
- (variables.get("stratum", 0),
- ptype,
- Ntpq.prettyinterval(now if last_sync is None else int(now - lfptofloat(last_sync))),
- Ntpq.prettyinterval(poll_sec),
- reach, estdelay, estoffset,
- jd))
- return True
-
- def __dogetpeers(self, header, associd):
- try:
- variables = self.session.readvar(associd)
- except Mode6Exception as e:
- print(e.message)
- return False
- if not variables:
- if len(self.chosts) > 1:
- self.warn("server=%s ", self.session.hostname)
- self.warn("***No information returned for association %d\n" \
- % associd)
- return False;
- return self.__doprintpeers(variables, header, associd);
-
- def __dopeers(self, showall, header):
+ def __dopeers(self, showall, mode):
if not self.__dogetassoc():
return
+ report = PeerSummary(mode,
+ self.pktversion,
+ self.showhostnames,
+ self.wideremote)
maxhostlen = 0
if len(self.chosts) > 1:
maxhostlen = max([len(host) for (host, _af) in self.chosts])
self.say("%-*.*s " % \
(maxhostlen, maxhostlen+1, "server"))
- self.say(header)
- self.say(("=" * (maxhostlen + 78 + self.horizontal_slack)) + "\n")
+ self.say(report.header() + "\n")
+ if len(self.chosts) > 1:
+ maxhostlen = max([len(host) for (host, _af) in self.chosts])
+ self.say("=" * (maxhostlen + 1))
+ self.say(("=" * report.width()) + "\n")
for peer in self.peers:
if not showall and \
not (CTL_PEER_STATVAL(peer.status)
@@ -475,8 +290,21 @@ usage: help [ command ]
if self.debug:
self.warn(stderr, "eliding [%d]\n" % peer.associd)
continue
- if not self.__dogetpeers(header, peer.associd):
+ try:
+ variables = self.session.readvar(peer.associd)
+ except Mode6Exception as e:
+ print(e.message)
return
+ if not variables:
+ if len(self.chosts) > 1:
+ self.warn("server=%s ", self.session.hostname)
+ self.warn("***No information returned for association %d\n" \
+ % associd)
+ continue
+ if len(self.chosts) > 1:
+ self.say(PeerSummary.high_truncate(self.session.hostname, maxhostlen)+ " ")
+ self.say(report.summary(self.session.rstatus,
+ variables, peer.associd))
def __assoc_valid(self, line, required=False):
"Process a numeric associd or index."
@@ -1220,7 +1048,7 @@ usage: pstats assocID
def do_peers(self, line):
"obtain and print a list of the server's peers [IP version]"
- self.__dopeers(showall=False, header=self.__peerheader)
+ self.__dopeers(showall=False, mode="peers")
def help_peers(self):
self.say("""\
@@ -1230,7 +1058,7 @@ usage: peers
def do_apeers(self, line):
"obtain and print a list of the server's peers and their assocIDs [IP version]"
- self.__dopeers(showall=False, header=self.__apeerheader)
+ self.__dopeers(showall=False, mode="apeers")
def help_apeers(self):
self.say("""\
@@ -1240,7 +1068,7 @@ usage: apeers
def do_lpeers(self, line):
"obtain and print a list of all peers and clients [IP version]"
- self.__dopeers(showall=True, header=self.__peerheader)
+ self.__dopeers(showall=True, mode="peers")
def help_lpeers(self):
self.say("""\
@@ -1250,7 +1078,7 @@ usage: lpeers
def do_opeers(self, line):
"print peer list the old way, with dstadr shown rather than refid [IP version]"
- self.__dopeers(showall=False, header=self.__opeerheader)
+ self.__dopeers(showall=False, mode="opeers")
def help_opeers(self):
self.say("""\
=====================================
pylib/util.py
=====================================
--- a/pylib/util.py
+++ b/pylib/util.py
@@ -5,6 +5,10 @@ from __future__ import print_function
import socket
import sys
import subprocess
+import time
+import ntp.ntpc
+
+from ntp.packet import *
def canonicalize_dns(hostname):
portsuffix = ""
@@ -42,4 +46,202 @@ def termsize():
else:
return (24, 80)
+class PeerSummary:
+ "Reusable report generator for peer statistics"
+ def __init__(self, displaymode, pktversion, showhostnames, wideremote):
+ self.displaymode = displaymode # peers/apeers.opeers
+ self.pktversion = pktversion # interpretation of flash bits
+ self.showhostnames = showhostnames # If false, display numeric IPs
+ self.wideremote = wideremote # show wide remote names?
+ # By default, the peer spreadsheet layout is designed so lines just
+ # fit in 80 characters. This tells us how much extra horizontal space
+ # we have available on a wider terminal emulator
+ self.horizontal_slack = termsize()[1] - 80
+ # Peer spreadsheet column widths
+ self.namewidth = 15 + self.horizontal_slack
+ self.refidwidth = 15
+ # Compute peer spreadsheet headers
+ self.__remote = " remote ".ljust(self.namewidth)
+ self.__common = "st t when poll reach delay offset "
+ self.__header = None
+
+ @staticmethod
+ def prettyinterval(diff):
+ "Print an interval in natural time units."
+ if diff <= 0:
+ return "-"
+ if diff <= 2048:
+ return str(diff)
+ diff = (diff + 29) / 60
+ if diff <= 300:
+ return "%dm" % diff
+ diff = (diff + 29) / 60
+ if diff <= 96:
+ return "%dh" % diff
+ diff = (diff + 11) / 24
+ return "%dd" % diff
+
+ @staticmethod
+ def high_truncate(hostname, maxlen):
+ "Truncate on the left using leading _ to indicate 'more'."
+ # Used for local IPv6 addresses, best distinguished by low bits
+ if len(hostname) <= maxlen:
+ return hostname
+ else:
+ return '-' + hostname[-maxlen+1:]
+
+ def header(self):
+ "Column headers for peer display"
+ if self.displaymode == "apeers":
+ self.__header = self.__remote + \
+ " refid assid ".ljust(self.refidwidth) + \
+ self.__common + "jitter"
+ elif self.displaymode == "opeers":
+ self.__header = self.__remote + \
+ " local ".ljust(self.refidwidth) + \
+ self.__common + " disp"
+ else:
+ self.__header = self.__remote + \
+ " refid ".ljust(self.refidwidth) + \
+ self.__common + "jitter"
+ return self.__header
+
+ def width(self):
+ "Width of display"
+ return 78 + self.horizontal_slack
+
+ def summary(self, rstatus, variables, associd):
+ "Peer status summary line."
+ hmode = 0
+ srchost = None
+ srcport = 0
+ srcadr = None
+ dstadr_refid = ""
+ ppoll = 0
+ hpoll = 0
+ reach = 0
+ ptype = '?'
+ have_jitter = False
+ clock_name = ''
+
+ now = time.time()
+
+ for (name, value) in variables.items():
+ if name in ("srcadr", "peeradr"):
+ srcadr = value
+ elif name == "srchost":
+ srchost = value
+ elif name == "dstadr":
+ # The C code tried to get a fallback pytpe from this in case
+ # the hmode field was not included
+ if "local" in self.__header:
+ dstadr_refid = value
+ elif name == "hmode":
+ hmode = value
+ elif name == "refid":
+ # The C code for this looked crazily overelaborate. Best
+ # guess is that it was designed to deal with formats that
+ # no longer occur in this field.
+ if "refid" in self.__header:
+ dstadr_refid = value
+ elif name == "hpoll":
+ hpoll = value
+ if hpoll < 0:
+ hpoll = NTP_MINPOLL
+ elif name == "ppoll":
+ ppoll = value
+ if ppoll < 0:
+ ppoll = NTP_MINPOLL
+ elif name == "reach":
+ # Shipped as hex, displayed in octal
+ reach = value
+ elif name == "delay":
+ estdelay = value
+ elif name == "offset":
+ estoffset = value
+ elif name == "jitter":
+ if "jitter" in self.__header:
+ estjitter = value
+ have_jitter = True
+ elif name == "rootdisp" or name == "dispersion":
+ estdisp = value
+ elif name == "rec":
+ rec = value # l_fp timestamp
+ elif name == "srcport" or name == "peerport":
+ srcport = value
+ elif name == "reftime":
+ reftime = value # l_fp timestamp
+ if hmode == MODE_BCLIENT:
+ # broadcastclient or multicastclient
+ ptype = 'b'
+ elif hmode == MODE_BROADCAST:
+ # broadcast or multicast server
+ if srcadr.startswith("224."): # IANA multicast address prefix
+ ptype = 'M'
+ else:
+ ptype = 'B'
+ elif hmode == MODE_CLIENT:
+ if srchost and '(' in srchost:
+ ptype = 'l' # local refclock
+ elif dstadr_refid == "POOL":
+ ptype = 'p' # pool
+ elif srcadr.startswith("224."):
+ ptype = 'a' # manycastclient
+ else:
+ ptype = 'u' # unicast
+ elif hmode == MODE_ACTIVE:
+ ptype = 's' # symmetric active
+ elif hmode == MODE_PASSIVE:
+ ptype = 'S' # symmetric passive
+
+ #
+ # Got everything, format the line
+ #
+ line = ""
+ poll_sec = 1 << min(ppoll, hpoll)
+ if self.pktversion > NTP_OLDVERSION:
+ c = " x.-+#*o"[CTL_PEER_STATVAL(rstatus) & 0x7]
+ else:
+ c = " .+*"[CTL_PEER_STATVAL(rstatus) & 0x3]
+ # Source host or clockname
+ if srchost != None:
+ clock_name = srchost
+ elif self.showhostnames:
+ clock_name = canonicalize_dns(srcadr)
+ else:
+ clock_name = srcadr
+ if self.wideremote and len(clock_name) > self.namewidth:
+ line += ("%c%s\n" % (c, clock_name))
+ line + (" " * (self.namewidth + 2))
+ else:
+ line += ("%c%-*.*s " % \
+ (c, self.namewidth, self.namewidth, clock_name[:self.namewidth]))
+ # Destination address, assoc ID or refid.
+ assocwidth = 7 if self.displaymode == "apeers" else 0
+ if "." not in dstadr_refid and ":" not in dstadr_refid:
+ dstadr_refid = "." + dstadr_refid + "."
+ if assocwidth and len(dstadr_refid) >= self.refidwidth - assocwidth:
+ visible = "..."
+ else:
+ visible = dstadr_refid
+ line += self.high_truncate(visible, self.refidwidth)
+ if self.displaymode == "apeers":
+ line += (" " * (self.refidwidth - len(visible) - assocwidth + 1))
+ line += ("%-6d" % (associd))
+ else:
+ line += (" " * (self.refidwidth - len(visible)))
+ # The rest of the story
+ last_sync = variables.get("rec") or variables.get("reftime")
+ jd = estjitter if have_jitter else estdisp
+ jd = " -" if jd >= 999 else ("%7.3f" % jd)
+ line += (
+ " %2ld %c %4.4s %4.4s %3lo %7.3f %8.3f %s\n" % \
+ (variables.get("stratum", 0),
+ ptype,
+ PeerSummary.prettyinterval(now if last_sync is None else int(now - ntp.ntpc.lfptofloat(last_sync))),
+ PeerSummary.prettyinterval(poll_sec),
+ reach, estdelay, estoffset,
+ jd))
+ return line
+
# end
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/44fa6c229ec38c6bdb33ae60418f071ab46f2764...5f0de4cdb4fc63a4b8be1ccb3436685aa63936c3
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ntpsec.org/pipermail/vc/attachments/20161027/cb6d439f/attachment.html>
More information about the vc
mailing list