[Git][NTPsec/ntpsec][master] 5 commits: ntpq: Re-add synthetic garbage for group, then single value grab fails

James Browning (@jamesb_fe80) gitlab at mg.gitlab.com
Thu Feb 10 16:00:53 UTC 2022



James Browning pushed to branch master at NTPsec / ntpsec


Commits:
9d7ef0ca by James Browning at 2022-02-10T13:34:23+00:00
ntpq: Re-add synthetic garbage for group, then single value grab fails


- - - - -
1c7defb7 by James Browning at 2022-02-10T13:34:23+00:00
ntp.util: Add duration&packet count helpers/tests, uptime now lambda.


- - - - -
e6cc59fd by James Browning at 2022-02-10T13:34:23+00:00
ntpw: Attach yet unused duration/packet count helpers bar sysstats..


- - - - -
0e9ceccd by James Browning at 2022-02-10T13:34:23+00:00
ntpq: make sysstats 3-5 columns wide, much else 2-3.


- - - - -
fb5cb3a1 by James Browning at 2022-02-10T13:34:23+00:00
ntp.util, ntpq: clip some trailing whitespace.


- - - - -


3 changed files:

- ntpclients/ntpq.py
- pylib/util.py
- tests/pylib/test_util.py


Changes:

=====================================
ntpclients/ntpq.py
=====================================
@@ -53,7 +53,8 @@ NTP_LFP = 0x7     # NTP timestamp
 NTP_MODE = 0x8    # peer mode
 NTP_2BIT = 0x9    # leap bits
 NTP_FLOAT = 0xa   # Float value
-NTP_UPTIME = 0xb  # uptime in days H:M:S (no frac)
+NTP_UPTIME = 0xb  # uptime in daysD H:M:S (usually no frac)
+NTP_PACKETS = 0xc # packet counts
 
 
 class Ntpq(cmd.Cmd):
@@ -415,7 +416,7 @@ usage: timeout [ msec ]
                             items.append((name, (value, rawvalue)))
                     except ntp.packet.ControlException as e:
                         if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
-                            items.append((var, "???"))
+                            items.append((var, ("???", None)))
                             continue
                         raise e
                 queried = ntp.util.OrderedDict(items)
@@ -429,6 +430,7 @@ usage: timeout [ msec ]
             self.say(self.session.response)
             return
         try:
+            runs, runl = None, None
             for (name, legend, fmt) in variables:
                 if name not in queried:
                     continue
@@ -437,19 +439,34 @@ usage: timeout [ msec ]
                 value2 = queried[name + '_r'][0]
                 rawvalue2 = queried[name + '_r'][1]
                 if fmt in (NTP_UINT, NTP_INT, NTP_FLOAT):
-                    if self.showunits:
-                        displayvalue = ntp.util.unitifyvar(rawvalue, name)
-                        displayvalue2 = ntp.util.unitifyvar(rawvalue2, name)
+                    if self.showunits and isinstance(rawvalue, (int, float)):
+                        display = ntp.util.unitifyvar(rawvalue, name)
                     else:
-                        displayvalue = value
-                        displayvalue2 = value2
-                    self.say("%13s \t%9d\t%9d\n" %
-                             (legend, displayvalue, displayvalue2))
+                        display = value
+                    if self.showunits and isinstance(rawvalue2, (int, float)):
+                        display2 = ntp.util.unitifyvar(rawvalue2, name)
+                    else:
+                        display2 = value2
+                    self.say("%13s   %12s %12s\n" %
+                             (legend, display, display2))
+                elif fmt == NTP_PACKETS:
+                    self.say(
+                        "{0:<13} {1[0]:>15} {2[0]:>15} {1[1]:>12}{1[2]:<3} {2[1]:>12}{2[2]:<3}\n".format(
+                            legend,
+                            ntp.util.packetize(value, runs),
+                            ntp.util.packetize(
+                                value2, runl, periodized=True, clipdigits=3
+                            ),
+                        )
+                    )
                 elif fmt == NTP_UPTIME:
-                    self.say("%13s  %s\t%s\n" % (legend, ntp.util.prettyuptime(
-                        value), ntp.util.prettyuptime(value2)))
+                    runs, display = ntp.util.periodize(value)
+                    runl, display2 = ntp.util.periodize(value2)
+                    self.say(
+                        "{0:<13} {1:>15} {2:>15}\n".format(legend, display, display2)
+                    )
                 else:
-                    self.warn("unexpected vc type %s for %s, value %s"
+                    self.warn("unexpected vc type %s for %s, value %s %s    "
                               % (fmt, name, value, value2))
         except KeyboardInterrupt:
             self.warn("display interrupted")
@@ -495,13 +512,14 @@ usage: timeout [ msec ]
                      % (associd, self.session.rstatus,
                         ntp.ntpc.statustoa(statype, self.session.rstatus)))
         try:
+            run = 1
             for (name, legend, fmt) in variables:
                 if name not in queried:
                     continue
                 value = queried[name][0]
                 rawvalue = queried[name][1]
                 if fmt in (NTP_ADD, NTP_ADP):
-                    if self.showhostnames & 1:  # if & 1, display names 
+                    if self.showhostnames & 1:  # if & 1, display names
                         if self.debug:
                             self.say("DNS lookup begins...")
                         value = ntp.util.canonicalize_dns(
@@ -517,7 +535,7 @@ usage: timeout [ msec ]
                         displayvalue = ntp.util.unitifyvar(rawvalue, name)
                     else:
                         displayvalue = value
-                    self.say("%s  %s\n" % (legend, displayvalue))
+                    self.say("%13s   %13s\n" % (legend, displayvalue))
                 elif fmt == NTP_LFP:
                     self.say("%s  %s\n" % (legend, ntp.ntpc.prettydate(value)))
                 elif fmt == NTP_2BIT:
@@ -533,6 +551,15 @@ usage: timeout [ msec ]
                         self.say("%s  %s\n" % (legend, modes[value]))
                     except IndexError:
                         self.say("%s  %s%d\n" % (legend, "mode#", value))
+                elif fmt == NTP_PACKETS:
+                    self.say(
+                        "{0:<13} {1[0]:>15} {1[1]:>8}{1[2]:<3}\n".format(
+                            legend, ntp.util.packetize(value, run)
+                        )
+                    )
+                elif fmt == NTP_UPTIME:
+                    run, display = ntp.util.periodize(value)
+                    self.say("{0:<13} {1:>15}\n".format(legend, display))
                 else:
                     self.warn("unexpected vc type %s for %s, value %s"
                               % (fmt, name, value))
@@ -1139,8 +1166,8 @@ usage: cv [ assocID ] [ name=value[,...] ]
         pstats = (
             ("srcadr", "remote host:          ", NTP_ADD),
             ("dstadr", "local address:        ", NTP_ADD),
-            ("timerec", "time last received:   ", NTP_INT),
-            ("timer", "time until next send: ", NTP_INT),
+            ("timerec", "time last received:   ", NTP_UPTIME),
+            ("timer", "time until next send: ", NTP_UPTIME),
             ("timereach", "reachability change:  ", NTP_INT),
             ("sent", "packets sent:         ", NTP_INT),
             ("received", "packets received:     ", NTP_INT),
@@ -1524,21 +1551,24 @@ usage: kerninfo
     def do_sysstats(self, _line):
         "display system uptime and packet counts"
         sysstats = (
-            ("ss_uptime", "uptime:               ", NTP_INT),
+            ("ss_uptime", "uptime:               ", NTP_UPTIME),
             ("ss_numctlreq", "control requests:     ", NTP_INT),
+        )
+        sysstats2 = (
             ("ss_reset", "sysstats reset:       ", NTP_UPTIME),
-            ("ss_received", "packets received:     ", NTP_INT),
-            ("ss_thisver", "current version:      ", NTP_INT),
-            ("ss_oldver", "older version:        ", NTP_INT),
-            ("ss_badformat", "bad length or format: ", NTP_INT),
-            ("ss_badauth", "authentication failed:", NTP_INT),
-            ("ss_declined", "declined:             ", NTP_INT),
-            ("ss_restricted", "restricted:           ", NTP_INT),
-            ("ss_limited", "rate limited:         ", NTP_INT),
-            ("ss_kodsent", "KoD responses:        ", NTP_INT),
-            ("ss_processed", "processed for time:   ", NTP_INT),
+            ("ss_received", "packets received:     ", NTP_PACKETS),
+            ("ss_thisver", "current version:      ", NTP_PACKETS),
+            ("ss_oldver", "older version:        ", NTP_PACKETS),
+            ("ss_badformat", "bad length or format: ", NTP_PACKETS),
+            ("ss_badauth", "authentication failed:", NTP_PACKETS),
+            ("ss_declined", "declined:             ", NTP_PACKETS),
+            ("ss_restricted", "restricted:           ", NTP_PACKETS),
+            ("ss_limited", "rate limited:         ", NTP_PACKETS),
+            ("ss_kodsent", "KoD responses:        ", NTP_PACKETS),
+            ("ss_processed", "processed for time:   ", NTP_PACKETS),
         )
         self.collect_display(associd=0, variables=sysstats, decodestatus=False)
+        self.collect_display2(variables=sysstats2)
 
     def help_sysstats(self):
         self.say("""\
@@ -1557,8 +1587,8 @@ usage: sysstats
             ("mru_deepest",     "peak addresses:       ", NTP_INT),
             ("mru_maxdepth",    "maximum addresses:    ", NTP_INT),
             ("mru_mindepth",    "reclaim above count:  ", NTP_INT),
-            ("mru_maxage",      "reclaim maxage:       ", NTP_INT),
-            ("mru_minage",      "reclaim minage:       ", NTP_INT),
+            ("mru_maxage",      "reclaim maxage:       ", NTP_UPTIME),
+            ("mru_minage",      "reclaim minage:       ", NTP_UPTIME),
             ("mru_mem",         "kilobytes:            ", NTP_INT),
             ("mru_maxmem",      "maximum kilobytes:    ", NTP_INT),
             ("mru_exists",      "alloc: exists:        ", NTP_INT),
@@ -1566,7 +1596,7 @@ usage: sysstats
             ("mru_recycleold",  "alloc: recycle old:   ", NTP_INT),
             ("mru_recyclefull", "alloc: recycle full:  ", NTP_INT),
             ("mru_none",        "alloc: none:          ", NTP_INT),
-            ("mru_oldest_age",  "age of oldest slot:   ", NTP_INT),
+            ("mru_oldest_age",  "age of oldest slot:   ", NTP_UPTIME),
         )
         self.collect_display(associd=0, variables=monstats, decodestatus=False)
 
@@ -1581,19 +1611,19 @@ usage: monstats
     def do_authinfo(self, _line):
         "display symmetric authentication counters"
         authinfo = (
-            ("authreset",          "time since reset:    ", NTP_INT),
+            ("authreset",          "time since reset:    ", NTP_UPTIME),
             ("authkeys",           "stored keys:         ", NTP_INT),
             ("authfreek",          "free keys:           ", NTP_INT),
             ("authklookups",       "key lookups:         ", NTP_INT),
             ("authknotfound",      "keys not found:      ", NTP_INT),
-            ("authencrypts",       "encryptions:         ", NTP_INT),
-            ("authdigestencrypts", "digest encryptions:  ", NTP_INT),
-            ("authcmacencrypts",   "CMAC encryptions:    ", NTP_INT),
-            ("authdecrypts",       "decryptions:         ", NTP_INT),
-            ("authdigestdecrypts", "digest decryptions:  ", NTP_INT),
-            ("authdigestfails",    "digest failures:     ", NTP_INT),
-            ("authcmacdecrypts",   "CMAC decryptions:    ", NTP_INT),
-            ("authcmacfails",      "CMAC failures:       ", NTP_INT),
+            ("authencrypts",       "encryptions:         ", NTP_PACKETS),
+            ("authdigestencrypts", "digest encryptions:  ", NTP_PACKETS),
+            ("authcmacencrypts",   "CMAC encryptions:    ", NTP_PACKETS),
+            ("authdecrypts",       "decryptions:         ", NTP_PACKETS),
+            ("authdigestdecrypts", "digest decryptions:  ", NTP_PACKETS),
+            ("authdigestfails",    "digest failures:     ", NTP_PACKETS),
+            ("authcmacdecrypts",   "CMAC decryptions:    ", NTP_PACKETS),
+            ("authcmacfails",      "CMAC failures:       ", NTP_PACKETS),
             # Old variables no longer supported.
             # Interesting if looking at an old system.
             ("authkuncached",      "uncached keys:       ", NTP_INT),
@@ -1642,16 +1672,16 @@ usage: ntsinfo
     def do_iostats(self, _line):
         "display network input and output counters"
         iostats = (
-            ("iostats_reset", "time since reset:     ", NTP_INT),
+            ("iostats_reset", "time since reset:     ", NTP_UPTIME),
             ("total_rbuf", "receive buffers:      ", NTP_INT),
             ("free_rbuf", "free receive buffers: ", NTP_INT),
             ("used_rbuf", "used receive buffers: ", NTP_INT),
             ("rbuf_lowater", "low water refills:    ", NTP_INT),
-            ("io_dropped", "dropped packets:      ", NTP_INT),
-            ("io_ignored", "ignored packets:      ", NTP_INT),
-            ("io_received", "received packets:     ", NTP_INT),
-            ("io_sent", "packets sent:         ", NTP_INT),
-            ("io_sendfailed", "packet send failures: ", NTP_INT),
+            ("io_dropped", "dropped packets:      ", NTP_PACKETS),
+            ("io_ignored", "ignored packets:      ", NTP_PACKETS),
+            ("io_received", "received packets:     ", NTP_PACKETS),
+            ("io_sent", "packets sent:         ", NTP_PACKETS),
+            ("io_sendfailed", "packet send failures: ", NTP_PACKETS),
             ("io_wakeups", "input wakeups:        ", NTP_INT),
             ("io_goodwakeups", "useful input wakeups: ", NTP_INT),
         )
@@ -1668,7 +1698,7 @@ usage: iostats
     def do_timerstats(self, line):
         "display interval timer counters"
         timerstats = (
-            ("timerstats_reset", "time since reset:  ", NTP_INT),
+            ("timerstats_reset", "time since reset:  ", NTP_UPTIME),
             ("timer_overruns", "timer overruns:    ", NTP_INT),
             ("timer_xmts", "calls to transmit: ", NTP_INT),
         )


=====================================
pylib/util.py
=====================================
@@ -1221,7 +1221,7 @@ class MRUSummary:
         self.debug = debug
         self.logfp = logfp
         self.now = None
-        self.showhostnames = showhostnames  # if & 1, display names 
+        self.showhostnames = showhostnames  # if & 1, display names
         self.wideremote = wideremote
 
     header = " lstint avgint rstr r m v  count    score   drop rport remote address"
@@ -1435,11 +1435,42 @@ except ImportError:  # pragma: no cover
                 yield key
 
 
-def prettyuptime(uptime):
-    result = ''
-    if uptime >= 86400:
-        result += '%dD ' % (uptime // 86400)
-    result += '%02d:%02d:%02d' % ((uptime % 86400) //
-                                  3600, (uptime % 3600) // 60, uptime % 60)
-    return result
+def packetize(packets, period, clipdigits=0, periodized=False):
+    """Given a number of packets and a duration (s) return a tuple.
+
+    return the packet quantity, and a two part rate in packets/seconds
+    or seconds/packet. On error the latter fields should be blank, the
+    first the number of packets if zero otherwise unhelpful text."""
+    if not isinstance(packets, int):
+        return ("???", "", "")
+    if packets == 0 or not isinstance(period, (int, float)):
+        return (packets, "", "")
+    if packets > period:
+        return (packets, round(packets / period, clipdigits), "p/s")
+    if periodized:
+        return (packets, periodize(period / packets, clipdigits)[1], "/p")
+    return (packets, round(period / packets, clipdigits), "s/p")
+
+
+def periodize(period, clipdigits=0):
+    """Given a number of seconds, return number and pretty string.
+
+    On error return None for the number and an unhelpful string."""
+    clip = clipdigits if isinstance(clipdigits, (int, float)) else 0
+    if not isinstance(period, (int, float)):
+        return (None, "???")
+    result = ""
+    _ = round(period, clip)
+    nperiod = int(_) if clip < 1 else _
+    if nperiod >= 86400:
+        result += "%dD " % (nperiod // 86400)
+    result += "%02d:%02d:%02d" % (
+        (nperiod % 86400) // 3600,
+        (nperiod % 3600) // 60,
+        nperiod % 60,
+    )
+    return (nperiod, result)
+
+
+uptime = lambda p: periodize(p)[1]
 # end


=====================================
tests/pylib/test_util.py
=====================================
@@ -2,6 +2,7 @@
 
 from __future__ import print_function
 
+import inspect
 import unittest
 import ntp.util
 import ntp.packet
@@ -1273,5 +1274,118 @@ class TestPeerSummary(unittest.TestCase):
         self.assertEqual(cls.polls, [])
 
 
+class NtpqRvInfoStats(unittest.TestCase):
+    """Test functions by controlling their diet and examining output.
+
+    Iterate through casees a 3 tuple, first element is a sub tuple
+    it gets fed to the subject. The second is compared to the output.
+    The third is a string to append to the test when things break.
+    """
+
+    periodic = 230258.509299404568402
+    packets = 9586785
+
+    def test_periodize(self):
+        """Test ntp.util.periodize by coqtavoric gavage and scatology."""
+        # def periodize(period, clipdigits=0)
+        cases = (
+            ((self.periodic, 5), (round(self.periodic, 5), "2D 15:57:38"), "normal"),
+            ((str(self.periodic), 5), (None, "???"), "period is str"),
+            (
+                (self.periodic, None),
+                (round(self.periodic), "2D 15:57:39"),
+                "clipdigits is None",
+            ),
+            # (('there', 'is'),('no', 'cake'),'broca'), # False test
+        )
+        shot_test(self, ntp.util.periodize, cases)
+
+    def test_packetize(self):
+        """Test ntp.util.packetize by coqtavoric gavage and scatology."""
+        # def packetize(packets, period, clipdigits=0, periodized=False)
+        cases = (
+            ((7.5, self.periodic), ("???", "", ""), "packets is float"),
+            ((0, self.periodic), (0, "", ""), "packets == 0"),
+            (
+                (self.packets, str(self.periodic)),
+                (self.packets, "", ""),
+                "period is str",
+            ),
+            (
+                (self.packets, self.periodic, 0),
+                (self.packets, 42.0, "p/s"),
+                "more packets, clipdigits == 0",
+            ),
+            (
+                (self.packets, self.periodic, 5),
+                (self.packets, 41.63488, "p/s"),
+                "more packets, clipdigits = 5",
+            ),
+            (
+                (self.packets // 200, self.periodic, 0, True),
+                (self.packets // 200, "00:00:05", "/p"),
+                "more time, clipdigits == 5, stringify",
+            ),
+            (
+                (self.packets // 200, self.periodic, 5, True),
+                (self.packets // 200, "00:00:04", "/p"),
+                "more time, clipdigits == 5, stringify",
+            ),
+            (
+                (self.packets // 200, self.periodic, 0, False),
+                (self.packets // 200, 5.0, "s/p"),
+                "more time, clipdigits == 5, float",
+            ),
+            (
+                (self.packets // 200, self.periodic, 5, False),
+                (self.packets // 200, 4.80376, "s/p"),
+                "more time, clipdigits == 5, float",
+            ),
+            # ((50, 10, 0, True),('the', 'cake', 'lies'),'broca') # False test
+        )
+        shot_test(self, ntp.util.packetize, cases)
+
+    def test_uptime(self):
+        # def uptime(period):
+        """Test ntp.util.uptime by coqtavoric gavage and scatology."""
+        cases = [
+            # [['cake'], 'you will be baked', 'broca'],
+            [[-2.6], "23:59:57", "-float"],
+            [[0.0], "00:00:00", "0float"],
+            [[2.3], "00:00:02", "+float"],
+            [[-2], "23:59:58", "-int"],
+            [[0], "00:00:00", "0int"],
+            [[2], "00:00:02", "+int"],
+        ]
+        shot_test(self, ntp.util.uptime, cases)
+
+
+def shot_test(classy, hook, cases):
+    if "msg" in inspect.getargspec(classy.assertEqual).args:
+        aeq = lambda r, l, d: classy.assertEqual(r, l, d)
+    else:
+        aeq = lambda r, l, d: classy.assertEqual(r, l)  # d
+
+    if hasattr(NtpqRvInfoStats, "subTest"):
+
+        def st(h, c):
+            for s in c:
+                with classy.subTest(s=s):
+                    aeq(h(*s[0]), s[1], s[2])
+
+    else:
+
+        def st(h, c):
+            l_e, l_r, m = [], [], []
+            for s in c:
+                i = h(*s[0])
+                l_r.append([i])
+                l_e.append([s[1]])
+                m.append([s[2]])
+            aeq(l_e, l_r, m)
+
+    st(hook, cases)
+
+
 if __name__ == '__main__':
     unittest.main()



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/2bf5e4f6f5b6658174201d7541efe5523d41c1ee...fb5cb3a150cd866dae3e9bd468de3094724c3443

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/2bf5e4f6f5b6658174201d7541efe5523d41c1ee...fb5cb3a150cd866dae3e9bd468de3094724c3443
You're receiving this email because of your account on gitlab.com.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20220210/aad523d5/attachment-0001.htm>


More information about the vc mailing list