Fix hlapi LCD to include `contextName`
Fixed hlapi LCD configurator to include `contextName`. Prior to this fix sending SNMPv3 TRAP with non-default `contextName` would fail. This change modifies the signature of the internal LCD methods.fix-missing-context-name-in-vacm
parent
c8d699ca0a
commit
d5def89ef1
|
@ -7,6 +7,8 @@ Revision 4.4.7, released 2018-11-XX
|
||||||
the stack frames (ultimately shown in traceback/debugger)
|
the stack frames (ultimately shown in traceback/debugger)
|
||||||
- Fixed hlapi/v3arch transport target caching to ensure transport targets
|
- Fixed hlapi/v3arch transport target caching to ensure transport targets
|
||||||
are different even if just timeout/retries options differ
|
are different even if just timeout/retries options differ
|
||||||
|
- Fixed hlapi LCD configurator to include `contextName`. Prior to this fix
|
||||||
|
sending SNMPv3 TRAP with non-default `contextName` would fail.
|
||||||
|
|
||||||
Revision 4.4.6, released 2018-09-13
|
Revision 4.4.6, released 2018-09-13
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
|
@ -560,12 +560,13 @@ def addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
|
||||||
|
|
||||||
def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
readSubTree=(), writeSubTree=(), notifySubTree=()):
|
readSubTree=(), writeSubTree=(), notifySubTree=(),
|
||||||
|
contextName=null):
|
||||||
(groupName, securityLevel, readView, writeView,
|
(groupName, securityLevel, readView, writeView,
|
||||||
notifyView) = __cookVacmUserInfo(snmpEngine, securityModel,
|
notifyView) = __cookVacmUserInfo(snmpEngine, securityModel,
|
||||||
securityName, securityLevel)
|
securityName, securityLevel)
|
||||||
delVacmGroup(snmpEngine, securityModel, securityName)
|
delVacmGroup(snmpEngine, securityModel, securityName)
|
||||||
delVacmAccess(snmpEngine, groupName, null, securityModel, securityLevel)
|
delVacmAccess(snmpEngine, groupName, contextName, securityModel, securityLevel)
|
||||||
if readSubTree:
|
if readSubTree:
|
||||||
delVacmView(
|
delVacmView(
|
||||||
snmpEngine, readView, readSubTree
|
snmpEngine, readView, readSubTree
|
||||||
|
@ -582,36 +583,40 @@ def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
|
||||||
# Obsolete shortcuts for add/delVacmUser() wrappers
|
# Obsolete shortcuts for add/delVacmUser() wrappers
|
||||||
|
|
||||||
def addRoUser(snmpEngine, securityModel, securityName, securityLevel, subTree):
|
def addRoUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
addVacmUser(snmpEngine, securityModel, securityName,
|
subTree, contextName=null):
|
||||||
securityLevel, subTree)
|
|
||||||
|
|
||||||
|
|
||||||
def delRoUser(snmpEngine, securityModel, securityName, securityLevel, subTree):
|
|
||||||
delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
|
||||||
subTree)
|
|
||||||
|
|
||||||
|
|
||||||
def addRwUser(snmpEngine, securityModel, securityName, securityLevel, subTree):
|
|
||||||
addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
subTree, subTree)
|
subTree, contextName=contextName)
|
||||||
|
|
||||||
|
|
||||||
def delRwUser(snmpEngine, securityModel, securityName, securityLevel, subTree):
|
def delRoUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
subTree, contextName=null):
|
||||||
delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
subTree, subTree)
|
subTree, contextName=contextName)
|
||||||
|
|
||||||
|
|
||||||
|
def addRwUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
subTree, contextName=null):
|
||||||
|
addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
subTree, subTree, contextName=contextName)
|
||||||
|
|
||||||
|
|
||||||
|
def delRwUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
subTree, contextName=null):
|
||||||
|
delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
|
subTree, subTree, contextName=contextName)
|
||||||
|
|
||||||
|
|
||||||
def addTrapUser(snmpEngine, securityModel, securityName,
|
def addTrapUser(snmpEngine, securityModel, securityName,
|
||||||
securityLevel, subTree):
|
securityLevel, subTree, contextName=null):
|
||||||
addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
(), (), subTree)
|
(), (), subTree, contextName=contextName)
|
||||||
|
|
||||||
|
|
||||||
def delTrapUser(snmpEngine, securityModel, securityName,
|
def delTrapUser(snmpEngine, securityModel, securityName,
|
||||||
securityLevel, subTree):
|
securityLevel, subTree, contextName=null):
|
||||||
delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
|
||||||
(), (), subTree)
|
(), (), subTree, contextName=contextName)
|
||||||
|
|
||||||
|
|
||||||
# Notification target setup
|
# Notification target setup
|
||||||
|
|
|
@ -148,7 +148,8 @@ def getCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||||
)
|
)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
future = asyncio.Future()
|
future = asyncio.Future()
|
||||||
|
|
||||||
|
@ -254,7 +255,8 @@ def setCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||||
)
|
)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
future = asyncio.Future()
|
future = asyncio.Future()
|
||||||
|
|
||||||
|
@ -366,7 +368,8 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||||
)
|
)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
future = asyncio.Future()
|
future = asyncio.Future()
|
||||||
|
|
||||||
|
@ -507,7 +510,8 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
(errorIndication, errorStatus, errorIndex, varBindsUnmade)
|
||||||
)
|
)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
future = asyncio.Future()
|
future = asyncio.Future()
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,8 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData,
|
||||||
)
|
)
|
||||||
|
|
||||||
notifyName = lcd.configure(
|
notifyName = lcd.configure(
|
||||||
snmpEngine, authData, transportTarget, notifyType
|
snmpEngine, authData, transportTarget, notifyType,
|
||||||
)
|
contextData.contextName)
|
||||||
|
|
||||||
future = asyncio.Future()
|
future = asyncio.Future()
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,8 @@ def getCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
snmpEngine, varBinds, lookupMib
|
snmpEngine, varBinds, lookupMib
|
||||||
), cbCtx)
|
), cbCtx)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
return cmdgen.GetCommandGenerator().sendVarBinds(
|
return cmdgen.GetCommandGenerator().sendVarBinds(
|
||||||
snmpEngine, addrName, contextData.contextEngineId,
|
snmpEngine, addrName, contextData.contextEngineId,
|
||||||
|
@ -232,7 +233,8 @@ def setCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
snmpEngine, varBinds, lookupMib
|
snmpEngine, varBinds, lookupMib
|
||||||
), cbCtx)
|
), cbCtx)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
return cmdgen.SetCommandGenerator().sendVarBinds(
|
return cmdgen.SetCommandGenerator().sendVarBinds(
|
||||||
snmpEngine, addrName, contextData.contextEngineId,
|
snmpEngine, addrName, contextData.contextEngineId,
|
||||||
|
@ -343,7 +345,9 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
varBindTable],
|
varBindTable],
|
||||||
cbCtx)
|
cbCtx)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
return cmdgen.NextCommandGenerator().sendVarBinds(
|
return cmdgen.NextCommandGenerator().sendVarBinds(
|
||||||
snmpEngine, addrName,
|
snmpEngine, addrName,
|
||||||
contextData.contextEngineId, contextData.contextName,
|
contextData.contextEngineId, contextData.contextName,
|
||||||
|
@ -483,7 +487,9 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
[vbProcessor.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in
|
[vbProcessor.unmakeVarBinds(snmpEngine, varBindTableRow, lookupMib) for varBindTableRow in
|
||||||
varBindTable], cbCtx)
|
varBindTable], cbCtx)
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
return cmdgen.BulkCommandGenerator().sendVarBinds(
|
return cmdgen.BulkCommandGenerator().sendVarBinds(
|
||||||
snmpEngine, addrName, contextData.contextEngineId,
|
snmpEngine, addrName, contextData.contextEngineId,
|
||||||
contextData.contextName, nonRepeaters, maxRepetitions,
|
contextData.contextName, nonRepeaters, maxRepetitions,
|
||||||
|
|
|
@ -131,7 +131,7 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData,
|
||||||
)
|
)
|
||||||
|
|
||||||
notifyName = lcd.configure(snmpEngine, authData, transportTarget,
|
notifyName = lcd.configure(snmpEngine, authData, transportTarget,
|
||||||
notifyType)
|
notifyType, contextData.contextName)
|
||||||
|
|
||||||
return ntforg.NotificationOriginator().sendVarBinds(
|
return ntforg.NotificationOriginator().sendVarBinds(
|
||||||
snmpEngine, notifyName,
|
snmpEngine, notifyName,
|
||||||
|
|
|
@ -8,6 +8,8 @@ from pysnmp.entity import config
|
||||||
from pysnmp import nextid, error
|
from pysnmp import nextid, error
|
||||||
from pysnmp.hlapi.auth import *
|
from pysnmp.hlapi.auth import *
|
||||||
|
|
||||||
|
from pyasn1.compat.octets import null
|
||||||
|
|
||||||
__all__ = ['CommandGeneratorLcdConfigurator',
|
__all__ = ['CommandGeneratorLcdConfigurator',
|
||||||
'NotificationOriginatorLcdConfigurator']
|
'NotificationOriginatorLcdConfigurator']
|
||||||
|
|
||||||
|
@ -24,17 +26,17 @@ class AbstractLcdConfigurator(object):
|
||||||
snmpEngine.setUserContext(**{cacheId: cache})
|
snmpEngine.setUserContext(**{cacheId: cache})
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
def configure(self, snmpEngine, authData, transportTarget, *options):
|
def configure(self, snmpEngine, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unconfigure(self, snmpEngine, authData=None):
|
def unconfigure(self, snmpEngine, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CommandGeneratorLcdConfigurator(AbstractLcdConfigurator):
|
class CommandGeneratorLcdConfigurator(AbstractLcdConfigurator):
|
||||||
cacheKeys = ['auth', 'parm', 'tran', 'addr']
|
cacheKeys = ['auth', 'parm', 'tran', 'addr']
|
||||||
|
|
||||||
def configure(self, snmpEngine, authData, transportTarget, *options):
|
def configure(self, snmpEngine, authData, transportTarget, contextName, **options):
|
||||||
cache = self._getCache(snmpEngine)
|
cache = self._getCache(snmpEngine)
|
||||||
if isinstance(authData, CommunityData):
|
if isinstance(authData, CommunityData):
|
||||||
if authData.communityIndex not in cache['auth']:
|
if authData.communityIndex not in cache['auth']:
|
||||||
|
@ -117,7 +119,7 @@ class CommandGeneratorLcdConfigurator(AbstractLcdConfigurator):
|
||||||
|
|
||||||
return addrName, paramsName
|
return addrName, paramsName
|
||||||
|
|
||||||
def unconfigure(self, snmpEngine, authData=None):
|
def unconfigure(self, snmpEngine, authData=None, contextName=null, **options):
|
||||||
cache = self._getCache(snmpEngine)
|
cache = self._getCache(snmpEngine)
|
||||||
if authData:
|
if authData:
|
||||||
if isinstance(authData, CommunityData):
|
if isinstance(authData, CommunityData):
|
||||||
|
@ -198,9 +200,9 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
||||||
cacheKeys = ['auth', 'name']
|
cacheKeys = ['auth', 'name']
|
||||||
_cmdGenLcdCfg = CommandGeneratorLcdConfigurator()
|
_cmdGenLcdCfg = CommandGeneratorLcdConfigurator()
|
||||||
|
|
||||||
def configure(self, snmpEngine, authData, transportTarget, *options):
|
def configure(self, snmpEngine, authData, transportTarget, notifyType,
|
||||||
|
contextName, **options):
|
||||||
cache = self._getCache(snmpEngine)
|
cache = self._getCache(snmpEngine)
|
||||||
notifyType = options and options[0] or 'trap'
|
|
||||||
notifyName = None
|
notifyName = None
|
||||||
|
|
||||||
# Create matching transport tags if not given by user. Not good!
|
# Create matching transport tags if not given by user. Not good!
|
||||||
|
@ -211,7 +213,8 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
||||||
if isinstance(authData, CommunityData) and not authData.tag:
|
if isinstance(authData, CommunityData) and not authData.tag:
|
||||||
authData.tag = transportTarget.tagList.split()[0]
|
authData.tag = transportTarget.tagList.split()[0]
|
||||||
|
|
||||||
addrName, paramsName = self._cmdGenLcdCfg.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = self._cmdGenLcdCfg.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextName, **options)
|
||||||
tagList = transportTarget.tagList.split()
|
tagList = transportTarget.tagList.split()
|
||||||
if not tagList:
|
if not tagList:
|
||||||
tagList = ['']
|
tagList = ['']
|
||||||
|
@ -230,7 +233,7 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
||||||
notifyType
|
notifyType
|
||||||
)
|
)
|
||||||
cache['name'][notifyNameKey] = notifyName, paramsName, 1
|
cache['name'][notifyNameKey] = notifyName, paramsName, 1
|
||||||
authDataKey = authData.securityName, authData.securityModel
|
authDataKey = authData.securityName, authData.securityModel, authData.securityLevel, contextName
|
||||||
if authDataKey in cache['auth']:
|
if authDataKey in cache['auth']:
|
||||||
authDataX, subTree, useCount = cache['auth'][authDataKey]
|
authDataX, subTree, useCount = cache['auth'][authDataKey]
|
||||||
cache['auth'][authDataKey] = authDataX, subTree, useCount + 1
|
cache['auth'][authDataKey] = authDataX, subTree, useCount + 1
|
||||||
|
@ -238,23 +241,24 @@ class NotificationOriginatorLcdConfigurator(AbstractLcdConfigurator):
|
||||||
subTree = (1, 3, 6)
|
subTree = (1, 3, 6)
|
||||||
config.addTrapUser(snmpEngine, authData.securityModel,
|
config.addTrapUser(snmpEngine, authData.securityModel,
|
||||||
authData.securityName, authData.securityLevel,
|
authData.securityName, authData.securityLevel,
|
||||||
subTree)
|
subTree, contextName=contextName)
|
||||||
cache['auth'][authDataKey] = authData, subTree, 1
|
cache['auth'][authDataKey] = authData, subTree, 1
|
||||||
|
|
||||||
return notifyName
|
return notifyName
|
||||||
|
|
||||||
def unconfigure(self, snmpEngine, authData=None):
|
def unconfigure(self, snmpEngine, authData=None, contextName=null, **options):
|
||||||
cache = self._getCache(snmpEngine)
|
cache = self._getCache(snmpEngine)
|
||||||
if authData:
|
if authData:
|
||||||
authDataKey = authData.securityName, authData.securityModel
|
authDataKey = authData.securityName, authData.securityModel, authData.securityLevel, contextName
|
||||||
if authDataKey in cache['auth']:
|
if authDataKey in cache['auth']:
|
||||||
authDataKeys = (authDataKey,)
|
authDataKeys = (authDataKey,)
|
||||||
else:
|
else:
|
||||||
raise error.PySnmpError('Unknown authData %s' % (authData,))
|
raise error.PySnmpError('Unknown authData %s' % (authData,))
|
||||||
else:
|
else:
|
||||||
authDataKeys = tuple(cache['auth'].keys())
|
authDataKeys = tuple(cache['auth'])
|
||||||
|
|
||||||
addrNames, paramsNames = self._cmdGenLcdCfg.unconfigure(snmpEngine, authData)
|
addrNames, paramsNames = self._cmdGenLcdCfg.unconfigure(
|
||||||
|
snmpEngine, authData, contextName, **options)
|
||||||
|
|
||||||
notifyAndParamsNames = [(cache['name'][x], x) for x in cache['name'].keys() if x[0] in paramsNames]
|
notifyAndParamsNames = [(cache['name'][x], x) for x in cache['name'].keys() if x[0] in paramsNames]
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,8 @@ def getCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
else:
|
else:
|
||||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
deferred = Deferred()
|
deferred = Deferred()
|
||||||
|
|
||||||
|
@ -246,7 +247,8 @@ def setCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
else:
|
else:
|
||||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
deferred = Deferred()
|
deferred = Deferred()
|
||||||
|
|
||||||
|
@ -377,7 +379,8 @@ def nextCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
else:
|
else:
|
||||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
deferred = Deferred()
|
deferred = Deferred()
|
||||||
|
|
||||||
|
@ -536,7 +539,8 @@ def bulkCmd(snmpEngine, authData, transportTarget, contextData,
|
||||||
else:
|
else:
|
||||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||||
|
|
||||||
addrName, paramsName = lcd.configure(snmpEngine, authData, transportTarget)
|
addrName, paramsName = lcd.configure(
|
||||||
|
snmpEngine, authData, transportTarget, contextData.contextName)
|
||||||
|
|
||||||
deferred = Deferred()
|
deferred = Deferred()
|
||||||
|
|
||||||
|
|
|
@ -137,9 +137,8 @@ def sendNotification(snmpEngine, authData, transportTarget, contextData,
|
||||||
else:
|
else:
|
||||||
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
deferred.callback((errorStatus, errorIndex, varBindsUnmade))
|
||||||
|
|
||||||
notifyName = lcd.configure(
|
notifyName = lcd.configure(snmpEngine, authData, transportTarget,
|
||||||
snmpEngine, authData, transportTarget, notifyType
|
notifyType, contextData.contextName)
|
||||||
)
|
|
||||||
|
|
||||||
def __trapFun(deferred):
|
def __trapFun(deferred):
|
||||||
deferred.callback((0, 0, []))
|
deferred.callback((0, 0, []))
|
||||||
|
|
Loading…
Reference in New Issue