Skip to content
GitLab
Menu
Why GitLab
Pricing
Contact Sales
Explore
Why GitLab
Pricing
Contact Sales
Explore
Sign in
Get free trial
Commits on Source (2)
In pybtpq, improve implementation of showhostnames.
· 87cd3df4
Eric S. Raymond
authored
Oct 27, 2016
87cd3df4
Refactoring step - make peer summary generator reusable.
· 5f0de4cd
Eric S. Raymond
authored
Oct 27, 2016
5f0de4cd
Hide whitespace changes
Inline
Side-by-side
ntpq/pyntpq
View file @
5f0de4cd
...
...
@@ -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
View file @
5f0de4cd
...
...
@@ -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