[Git][NTPsec/ntpsec][master] 2 commits: Cleaned up and updated comments
Ian Bruene
gitlab at mg.gitlab.com
Mon Oct 30 20:00:59 UTC 2017
Ian Bruene pushed to branch master at NTPsec / ntpsec
Commits:
6eabd1a7 by Ian Bruene at 2017-10-30T14:31:34-05:00
Cleaned up and updated comments
- - - - -
1af3c8fc by Ian Bruene at 2017-10-30T14:59:45-05:00
Added slots for agentx write callbacks
- - - - -
3 changed files:
- ntpclients/ntpsnmpd
- pylib/agentx.py
- tests/pylib/test_agentx.py
Changes:
=====================================
ntpclients/ntpsnmpd
=====================================
--- a/ntpclients/ntpsnmpd
+++ b/ntpclients/ntpsnmpd
@@ -37,187 +37,197 @@ class DataSource: # This may be broken up in future to be less NTP-specific
# certain edge cases
# OIDs are relative from ntp root
# ntpEntNotifications
- self.oidTree = {0: (None,
+ self.oidTree = {0: (None, None,
# ntpEntNotifModeChange
- {1: (None, None),
+ {1: (None, None, None),
# ntpEntNotifStratumChange
- 2: (None, None),
+ 2: (None, None, None),
# ntpEntNotifSyspeerChange
- 3: (None, None),
+ 3: (None, None, None),
# ntpEntNotifAddAssociation
- 4: (None, None),
+ 4: (None, None, None),
# ntpEntNotifRemoveAsociation
- 5: (None, None),
+ 5: (None, None, None),
# ntpEntNotifConfigChanged
- 6: (None, None),
+ 6: (None, None, None),
# ntpEntNotifLeapSecondAnnounced
- 7: (None, None),
+ 7: (None, None, None),
# ntpEntNotifHeartbeat
- 8: (None, None)}),
+ 8: (None, None, None)}),
# ntpSnmpMIBObjects
- 1: (None,
+ 1: (None, None,
# ntpEntInfo
- {1: (None,
+ {1: (None, None,
# ntpNetSoftwareName utf8str
{1: ((lambda oid:
self.cb_systemInfo(oid, "name")),
- None),
+ None, None),
# ntpEntSoftwareVersion utf8str
2: ((lambda oid:
self.cb_systemInfo(oid, "version")),
- None),
+ None, None),
# ntpEntSoftwareVendor utf8str
3: ((lambda oid:
self.cb_systemInfo(oid, "vendor")),
- None),
+ None, None),
# ntpEntSystemType utf8str
4: ((lambda oid:
self.cb_systemInfo(oid, "system")),
- None),
+ None, None),
# ntpEntTimeResolution uint32
- 5: (self.cb_timeResolution, None),
+ 5: (self.cb_timeResolution, None, None),
# ntpEntTimePrecision int32
- 6: (self.cb_timePrecision, None),
+ 6: (self.cb_timePrecision, None, None),
# ntpEntTimeDistance DisplayString
- 7: (self.cb_timeDistance, None)}),
+ 7: (self.cb_timeDistance, None, None)}),
# ntpEntStatus
- 2: (None,
+ 2: (None, None,
# ntpEntStatusCurrentMode INTEGER {...}
- {1: (self.cb_statusCurrentMode, None),
+ {1: (self.cb_statusCurrentMode, None, None),
# ntpEntStatusStratum NtpStratum
- 2: (self.cb_statusStratum, None),
+ 2: (self.cb_statusStratum, None, None),
# ntpEntStatusActiveRefSourceId
# uint32 (0..99999)
- 3: (self.cb_statusActiveRefSourceID, None),
+ 3: (self.cb_statusActiveRefSourceID,
+ None, None),
# ntpEntStatusActiveRefSourceName utf8str
- 4: (self.cb_statusActiveRefSourceName, None),
+ 4: (self.cb_statusActiveRefSourceName,
+ None, None),
# ntpEntStatusActiveOffset DisplayString
- 5: (self.cb_statusActiveOffset, None),
+ 5: (self.cb_statusActiveOffset, None, None),
# ntpEntStatusNumberOfRefSources
# unit32 (0..99)
- 6: (self.cb_statusNumRefSources, None),
+ 6: (self.cb_statusNumRefSources, None, None),
# ntpEntStatusDispersion DisplayString
- 7: (self.cb_statusDispersion, None),
+ 7: (self.cb_statusDispersion, None, None),
# ntpEntStatusEntityUptime TimeTicks
- 8: (self.cb_statusEntityUptime, None),
+ 8: (self.cb_statusEntityUptime, None, None),
# ntpEntStatusDateTime NtpDateTime
- 9: (self.cb_statusDateTime, None),
+ 9: (self.cb_statusDateTime, None, None),
# ntpEntStatusLeapSecond NtpDateTime
- 10: (self.cb_statusLeapSecond, None),
+ 10: (self.cb_statusLeapSecond, None, None),
# ntpEntStatusLeapSecondDirection
# int32 (-1..1)
- 11: (self.cb_statusLeapSecDirection, None),
+ 11: (self.cb_statusLeapSecDirection,
+ None, None),
# ntpEntStatusInPkts Counter32
- 12: (self.cb_statusInPkts, None),
+ 12: (self.cb_statusInPkts, None, None),
# ntpEntStatusOutPkts Counter32
- 13: (self.cb_statusOutPkts, None),
+ 13: (self.cb_statusOutPkts, None, None),
# ntpEntStatusBadVersion Counter32
- 14: (self.cb_statusBadVersion, None),
+ 14: (self.cb_statusBadVersion, None, None),
# ntpEntStatusProtocolError Counter32
- 15: (self.cb_statusProtocolError, None),
+ 15: (self.cb_statusProtocolError, None, None),
# ntpEntStatusNotifications Counter32
- 16: (self.cb_statusNotifications, None),
+ 16: (self.cb_statusNotifications, None, None),
# ntpEntStatPktModeTable
# SEQUENCE of NtpEntStatPktModeEntry
- 17: (None,
+ 17: (None, None,
# ntpEntStatPktModeEntry SEQUENCE {...}
- {1: (None,
+ {1: (None, None,
# ntpEntStatPktMode INTEGER {...}
- {1: (None, None),
+ {1: (None, None, None),
# ntpEntStatPktSent Counter32
- 2: (None, None),
+ 2: (None, None, None),
# ntpEntStatPktRecived Counter32
- 3: (None, None)})})}),
+ 3: (None, None, None)})})}),
# ntpAssociation
- 3: (None,
+ 3: (None, None,
# ntpAssociationTable
# SEQUENCE of NtpAssociationEntry
- {1: (None,
+ {1: (None, None,
# ntpAssociationEntry SEQUENCE {...}
- {1: (None,
+ {1: (None, None,
# ntpAssocId uint32 (1..99999)
- {1: (self.cb_assocID, None),
+ {1: (self.cb_assocID, None, None),
# ntpAssocName utf8str
- 2: (self.cb_assocName, None),
+ 2: (self.cb_assocName, None, None),
# ntpAssocRefId DisplayString
- 3: (self.cb_assocRefID, None),
+ 3: (self.cb_assocRefID, None, None),
# ntpAssocAddressType
# InetAddressType
- 4: (self.cb_assocAddrType, None),
+ 4: (self.cb_assocAddrType,
+ None, None),
# ntpAssocAddress
# InetAddress SIZE (4|8|16|20)
- 5: (self.cb_assocAddr, None),
+ 5: (self.cb_assocAddr, None, None),
# ntpAssocOffset DisplayString
- 6: (self.cb_assocOffset, None),
+ 6: (self.cb_assocOffset,
+ None, None),
# ntpAssocStratum NtpStratum
- 7: (self.cb_assocStratum, None),
+ 7: (self.cb_assocStratum,
+ None, None),
# ntpAssocStatusJitter DisplayString
8: (self.cb_assocStatusJitter,
- None),
+ None, None),
# ntpAssocStatusDelay DisplayString
- 9: (self.cb_assocStatusDelay, None),
+ 9: (self.cb_assocStatusDelay,
+ None, None),
# ntpAssocStatusDispersion
# DisplayString
10: (self.cb_assocStatusDisp,
- None)})}),
+ None, None)})}),
# ntpAssociationStatisticsTable
# SEQUENCE of ntpAssociationStatisticsEntry
- 2: (None,
+ 2: (None, None,
# ntpAssociationStatisticsEntry
# SEQUENCE {...}
- {1: (None,
+ {1: (None, None,
# ntpAssocStatInPkts Counter32
- {1: (self.cb_assocStatInPkts, None),
+ {1: (self.cb_assocStatInPkts,
+ None, None),
# ntpAssocStatOutPkts Counter32
2: (self.cb_assocStatOutPkts,
- None),
+ None, None),
# ntpAssocStatProtocolError
# Counter32
3: (self.cb_assocStatProtoErr,
- None)})})}),
+ None, None)})})}),
# ntpEntControl
- 4: (None,
+ 4: (None, None,
# ntpEntHeartbeatInterval unit32
- {1: (self.cb_entHeartbeatInterval, None),
+ {1: (self.cb_entHeartbeatInterval,
+ None, None),
# ntpEntNotifBits BITS {...}
- 2: (self.cb_entNotifBits, None)}),
+ 2: (self.cb_entNotifBits, None, None)}),
# ntpEntNotifObjects
- 5: (None,
+ 5: (None, None,
# ntpEntNotifMessage utf8str
- {1: (self.cb_entNotifMessage, None)})}),
+ {1: (self.cb_entNotifMessage, None, None)})}),
# ntpEntConformance
- 2: (None,
+ 2: (None, None,
# ntpEntCompliances
- {1: (None,
+ {1: (None, None,
# ntpEntNTPCompliance
- {1: (None, None),
+ {1: (None, None, None),
# ntpEntSNTPCompliance
- 2: (None, None)}),
+ 2: (None, None, None)}),
# ntpEntGroups
- 2: (None,
+ 2: (None, None,
# ntpEntObjectsGroup1 OBJECTS {...}
- {1: (None, None),
+ {1: (None, None, None),
# ntpEntObjectsGroup2 OBJECTS {...}
- 2: (None, None),
+ 2: (None, None, None),
# ntpEntNotifGroup NOTIFICATIONS {...}
- 3: (None, None)})})}
+ 3: (None, None, None)})})}
self.oidList = ntp.agentx.mibTree2List(self.oidTree, ntpRootOID)
def getOID(self, oid, acceptNext=False):
"Get the requested OID, or the next lexographical OID"
for node in self.oidList:
- if node[0] is None:
+ if node[0] is None: # No read callback
continue # skip over not yet implemented OIDs
- if (node[1] == oid):
+ if (node[2] == oid):
return node # (callback, oid)
- elif (node[1] > oid) and (acceptNext is True):
+ elif (node[2] > oid) and (acceptNext is True):
return node
# Nothing in the list
return (None, None)
def getNextOID(self, oid):
+ "Get the next lexographical OID"
for pos in range(len(self.oidList)):
- callback, current = self.oidList[pos]
+ read_callback, write_callback, current = self.oidList[pos]
if callback is None:
continue # skip over not yet implemented OIDs
if current <= oid:
@@ -225,19 +235,20 @@ class DataSource: # This may be broken up in future to be less NTP-specific
else:
return self.oidList[pos]
# Nothing after the supplied oid
- return (None, None)
+ return (None, None, None)
def getOIDsInRange(self, oidrange, firstOnly=False):
+ "Get a list of every (optionally the first) OID in a range"
oids = []
for node in self.oidList:
- if node[0] is None: # No callback
+ if node[0] is None: # No read callback
continue # skip over not yet implemented OIDs
- elif node[1] > oidrange.start: # Past the start, in the body
- if (oidrange.end is not None) and (node[1] >= oidrange.end):
+ elif node[2] > oidrange.start: # Past the start, in the body
+ if (oidrange.end is not None) and (node[2] >= oidrange.end):
break # Past the end of a bounded range
else:
oids.append(node) # Found a match
- elif (node[1].subids == oidrange.start.subids) and \
+ elif (node[2].subids == oidrange.start.subids) and \
(oidrange.start.include is True):
oids.append(node) # Inclusive search and a match at the start
else:
@@ -246,8 +257,10 @@ class DataSource: # This may be broken up in future to be less NTP-specific
break
return oids
+ # =============================================================
# Data retrevial callbacks start here
# comment divider lines represent not yet implemented callbacks
+ # =============================================================
#########################
@@ -470,21 +483,20 @@ class PacketControl:
dolog("Sent open packet\n", 1)
response = self.waitForResponse(openpkt, True)
self.sessionID = response.sessionID
- # Register the tree, just random crap for testing right now
- fooregister = ntp.agentx.RegisterPDU(True, self.sessionID, 1, 1,
- self.timeout, 1, ntpRootOID)
- self.sendPacket(fooregister, False)
- dolog("Sent register\n", 1)
- response = self.waitForResponse(fooregister, True)
+ # Register the tree
+ register = ntp.agentx.RegisterPDU(True, self.sessionID, 1, 1,
+ self.timeout, 1, ntpRootOID)
+ self.sendPacket(register, False)
+ dolog("Sent registration\n", 1)
+ response = self.waitForResponse(register, True)
def waitForResponse(self, opkt, ignoreSID=False):
+ "Wait for a response to a specific packet, dropping everything else"
while True:
self.packetEater()
while len(self.recievedPackets) > 0:
packet = self.recievedPackets.pop(0)
dolog("Waiting, got packet: " + repr(packet) + "\n\n", 3)
- # This will drop anything that is not the response
- # but if this method is being called we don't care
if packet.__class__ != ntp.agentx.ResponsePDU:
continue
haveit = (opkt.transactionID == packet.transactionID) and \
@@ -496,6 +508,7 @@ class PacketControl:
time.sleep(self.spinGap)
def packetEater(self):
+ "Slurps data from the input buffer and tries to parse packets from it"
self.pollSocket()
while True:
datalen = len(self.recievedData)
@@ -511,7 +524,7 @@ class PacketControl:
except (ax.ParseVersionError, ax.ParsePDUTypeError,
ax.ParseError) as e:
if e.header["type"] != ax.PDU_RESPONSE:
- # Response errors are silently dropped
+ # Response errors are silently dropped, per RFC
# Everything else sends an error response
resp = ax.ResponsePDU(e.header["flags"]["bigEndian"],
e.header["session_id"],
@@ -520,6 +533,8 @@ class PacketControl:
0, ax.RSPERR_PARSE_ERROR, 0)
self.sendPacket(resp, False)
# *Hopefully* the packet length was correct.....
+ # if not, all packets will be scrambled. Maybe dump the
+ # whole buffer if too many failures in a row?
self.receivedData = e.remainingData
def sendPacket(self, packet, expectsReply):
@@ -533,7 +548,7 @@ class PacketControl:
self.packetLog[index] = packet
def pollSocket(self):
- "Reads all currently available data from sock, non-blocking"
+ "Reads all currently available data from the socket, non-blocking"
data = ""
while True:
tmp = select.select([self.socket], [], [], 0)[0]
@@ -548,17 +563,19 @@ class PacketControl:
break
self.recievedData += data
+ # ==========================
# Packet handlers start here
+ # ==========================
def handle_GetPDU(self, packet):
binds = []
for oidr in packet.oidranges:
target = oidr.start
- callback, oid = self.database.getOID(target, False)
- if (oid != target) or (callback is None):
+ r_clbk, w_clbk, oid = self.database.getOID(target, False)
+ if (oid != target) or (r_clbk is None):
binds.append(ax.Varbind(ax.VALUE_NO_SUCH_OBJECT, target))
else:
- binds.append(callback(oid))
+ binds.append(r_clbk(oid))
# There should also be a situation that leads to noSuchInstance
# but I do not understand the requirements for that
# Need to implement genError
@@ -573,8 +590,8 @@ class PacketControl:
if len(oids) == 0: # Nothing found
binds.append(ax.Varbind(ax.VALUE_END_OF_MIB_VIEW, oidr.start))
else:
- callback, oid = oids[0]
- binds.append(callback(oid))
+ r_clbk, w_clbk, oid = oids[0]
+ binds.append(r_clbk(oid))
# Need to implement genError
resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID,
packet.packetID, 0, ax.ERR_NOERROR, 0, binds)
@@ -590,8 +607,8 @@ class PacketControl:
if len(oids) == 0: # Nothing found
binds.append(ax.Varbind(ax.VALUE_END_OF_MIB_VIEW, oidr.start))
else:
- callback, oid = oids[0]
- binds.append(callback(oid))
+ r_clbk, w_clbk, oid = oids[0]
+ binds.append(r_clbk(oid))
binds.append(varbind)
# Handle repeaters
for oidr in repeats:
@@ -599,8 +616,8 @@ class PacketControl:
if len(oids) == 0: # Nothing found
binds.append(ax.Varbind(ax.VALUE_END_OF_MIB_VIEW, oidr.start))
else:
- for callback, oid in oids[:packet.maxReps]:
- binds.append(callback(oid))
+ for r_clbk, w_clbk, oid in oids[:packet.maxReps]:
+ binds.append(r_clbk(oid))
resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID,
packet.packetID, 0, ax.ERR_NOERROR, 0, binds)
self.sendPacket(resp, False)
=====================================
pylib/agentx.py
=====================================
--- a/pylib/agentx.py
+++ b/pylib/agentx.py
@@ -1308,8 +1308,10 @@ def mibTree2List(mibtree, currentPath=()):
branches = list(mibtree.keys())
branches.sort()
for branch in branches:
- callback, tree = mibtree[branch]
- paths.append((callback, OID(currentPath + (branch,))))
+ read_callback, write_callback, tree = mibtree[branch]
+ paths.append((read_callback,
+ write_callback,
+ OID(currentPath + (branch,))))
branchPath = currentPath + (branch,)
paths += mibTree2List(tree, branchPath)
return tuple(paths)
@@ -1317,9 +1319,10 @@ def mibTree2List(mibtree, currentPath=()):
def mibList2Tree(miblist, rootPath=()):
"Takes a list of OIDs and inflates it into a tree"
+ # Tree node format: (read callback, write callback, sub-nodes)
tree = {}
for mibnode in miblist:
- callback, oid = mibnode[0], mibnode[1].subids
+ read_clbk, write_clbk, oid = mibnode[0], mibnode[1], mibnode[2].subids
rootlen = len(rootPath)
if oid[:rootlen] != rootPath: # OID not decended from the root, bail
raise ValueError("Node %s does not have root %s" %
@@ -1331,10 +1334,10 @@ def mibList2Tree(miblist, rootPath=()):
while oidPos < oidSize:
subid = oid[oidPos]
if subid not in branch: # First time at this position
- branch[subid] = (callback, None) # might be a leaf
- elif branch[subid][1] is None: # It isn't a leaf
- branch[subid] = (branch[subid][0], {})
- branch = branch[subid][1]
+ branch[subid] = (read_clbk, write_clbk, None) # might be a leaf
+ elif branch[subid][2] is None: # It isn't a leaf
+ branch[subid] = (branch[subid][0], branch[subid][1], {})
+ branch = branch[subid][2]
oidPos += 1
return tree
=====================================
tests/pylib/test_agentx.py
=====================================
--- a/tests/pylib/test_agentx.py
+++ b/tests/pylib/test_agentx.py
@@ -2507,35 +2507,41 @@ class TestNtpclientsNtpsnmpd(unittest.TestCase):
# Test empty tree
self.assertEqual(f({}), ())
# Test flat tree
- self.assertEqual(f({0: (None, None), 1: (None, None),
- 3: (None, None), 4: (None, None)}),
- ((None, x.OID((0,))), (None, x.OID((1,))),
- (None, x.OID((3,))), (None, x.OID((4,)))))
+ self.assertEqual(f({0: (None, None, None), 1: (None, None, None),
+ 3: (None, None, None), 4: (None, None, None)}),
+ ((None, None, x.OID((0,))),
+ (None, None, x.OID((1,))),
+ (None, None, x.OID((3,))),
+ (None, None, x.OID((4,)))))
# Test flat tree with root path
- self.assertEqual(f({0: (None, None), 1: (None, None),
- 3: (None, None), 4: (None, None)},
+ self.assertEqual(f({0: (None, None, None), 1: (None, None, None),
+ 3: (None, None, None), 4: (None, None, None)},
(42, 23)),
- ((None, x.OID((42, 23, 0))),
- (None, x.OID((42, 23, 1))),
- (None, x.OID((42, 23, 3))),
- (None, x.OID((42, 23, 4)))))
+ ((None, None, x.OID((42, 23, 0))),
+ (None, None, x.OID((42, 23, 1))),
+ (None, None, x.OID((42, 23, 3))),
+ (None, None, x.OID((42, 23, 4)))))
# Test nested tree
- self.assertEqual(f({0: (None, None),
- 2: (None,
- {0: (None, None), 1: (None, None)}),
- 3: (None,
- {5: (None,
- {0: (None, None), 2: (None, None)}),
- 6: (None, None)}),
- 4: (None, None)}),
- ((None, x.OID((0,))),
- (None, x.OID((2,))),
- (None, x.OID((2, 0))), (None, x.OID((2, 1))),
- (None, x.OID((3,))),
- (None, x.OID((3, 5))),
- (None, x.OID((3, 5, 0))), (None, x.OID((3, 5, 2))),
- (None, x.OID((3, 6))),
- (None, x.OID((4,)))))
+ self.assertEqual(f({0: (None, None, None),
+ 2: (None, None,
+ {0: (None, None, None),
+ 1: (None, None, None)}),
+ 3: (None, None,
+ {5: (None, None,
+ {0: (None, None, None),
+ 2: (None, None, None)}),
+ 6: (None, None, None)}),
+ 4: (None, None, None)}),
+ ((None, None, x.OID((0,))),
+ (None, None, x.OID((2,))),
+ (None, None, x.OID((2, 0))),
+ (None, None, x.OID((2, 1))),
+ (None, None, x.OID((3,))),
+ (None, None, x.OID((3, 5))),
+ (None, None, x.OID((3, 5, 0))),
+ (None, None, x.OID((3, 5, 2))),
+ (None, None, x.OID((3, 6))),
+ (None, None, x.OID((4,)))))
def test_mibList2Tree(self):
x = ntp.agentx
@@ -2544,35 +2550,40 @@ class TestNtpclientsNtpsnmpd(unittest.TestCase):
# Test empty tree
self.assertEqual(f(tuple()), {})
# Test flat tree
- self.assertEqual(f(((None, x.OID((0,))), (None, x.OID((1,))),
- (None, x.OID((3,))), (None, x.OID((4,))))),
- {0: (None, None), 1: (None, None),
- 3: (None, None), 4: (None, None)})
+ self.assertEqual(f(((None, None, x.OID((0,))),
+ (None, None, x.OID((1,))),
+ (None, None, x.OID((3,))),
+ (None, None, x.OID((4,))))),
+ {0: (None, None, None), 1: (None, None, None),
+ 3: (None, None, None), 4: (None, None, None)})
# Test flat tree with root path
- self.assertEqual(f(((None, x.OID((42, 23, 0))),
- (None, x.OID((42, 23, 1))),
- (None, x.OID((42, 23, 3))),
- (None, x.OID((42, 23, 4)))),
+ self.assertEqual(f(((None, None, x.OID((42, 23, 0))),
+ (None, None, x.OID((42, 23, 1))),
+ (None, None, x.OID((42, 23, 3))),
+ (None, None, x.OID((42, 23, 4)))),
(42, 23)),
- {0: (None, None), 1: (None, None),
- 3: (None, None), 4: (None, None)})
+ {0: (None, None, None), 1: (None, None, None),
+ 3: (None, None, None), 4: (None, None, None)})
# Test nested tree
- self.assertEqual(f(((None, x.OID((0,))),
- (None, x.OID((2,))),
- (None, x.OID((2, 0))), (None, x.OID((2, 1))),
- (None, x.OID((3,))),
- (None, x.OID((3, 5))),
- (None, x.OID((3, 5, 0))), (None, x.OID((3, 5, 2))),
- (None, x.OID((3, 6))),
- (None, x.OID((4,))))),
- {0: (None, None),
- 2: (None,
- {0: (None, None), 1: (None, None)}),
- 3: (None,
- {5: (None,
- {0: (None, None), 2: (None, None)}),
- 6: (None, None)}),
- 4: (None, None)})
+ self.assertEqual(f(((None, None, x.OID((0,))),
+ (None, None, x.OID((2,))),
+ (None, None, x.OID((2, 0))),
+ (None, None, x.OID((2, 1))),
+ (None, None, x.OID((3,))),
+ (None, None, x.OID((3, 5))),
+ (None, None, x.OID((3, 5, 0))),
+ (None, None, x.OID((3, 5, 2))),
+ (None, None, x.OID((3, 6))),
+ (None, None, x.OID((4,))))),
+ {0: (None, None, None),
+ 2: (None, None,
+ {0: (None, None, None), 1: (None, None, None)}),
+ 3: (None, None,
+ {5: (None, None,
+ {0: (None, None, None),
+ 2: (None, None, None)}),
+ 6: (None, None, None)}),
+ 4: (None, None, None)})
if __name__ == "__main__":
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/ca83d4ecde8e47a178bc9713ce01ec48cbcaa4f5...1af3c8fc5b77671142dad048f7024e819344c058
---
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/ca83d4ecde8e47a178bc9713ce01ec48cbcaa4f5...1af3c8fc5b77671142dad048f7024e819344c058
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/20171030/ad171017/attachment.html>
More information about the vc
mailing list