2016-12-29 153 views
5

我正在尝试编写一个python SNMP代理程序,可以将其嵌入到我的python应用程序中,以便可以通过OpenNMS远程监视应用程序。 OpenNMS期望代理实施HOST-RESOURCES-MIB查询两个字段hrSWRunNamehrSWRunStatus使用pySNMP编写SNMP代理(对于OpenNMS)

我拿了一个pysnmp的例子作为我的代码的基础,并编辑它,因为我认为有必要。生成的代码如下所示:

import logging 

from pysnmp import debug 
from pysnmp.carrier.asyncore.dgram import udp 
from pysnmp.entity import engine, config 
from pysnmp.entity.rfc3413 import cmdrsp, context 
from pysnmp.proto.api import v2c 
from pysnmp.smi import builder, instrum, exval 


# debug.setLogger(debug.Debug('all')) 

formatting = '[%(asctime)s-%(levelname)s]-(%(module)s) %(message)s' 
logging.basicConfig(level=logging.DEBUG, format=formatting,) 

logging.info("Starting....") 

# Create SNMP engine 
snmpEngine = engine.SnmpEngine() 

# Transport setup 

# UDP over IPv4 
config.addTransport(
    snmpEngine, 
    udp.domainName, 
    udp.UdpTransport().openServerMode(('localhost', 12345)) 
) 

# SNMPv2c setup 

# SecurityName <-> CommunityName mapping. 
config.addV1System(snmpEngine, 'my-area', 'public') 

# Allow read MIB access for this user/securityModels at VACM 
config.addVacmUser(snmpEngine, 
        2, 
        'my-area', 
        'noAuthNoPriv', 
        (1, 3, 6, 1, 2, 1), 
        (1, 3, 6, 1, 2, 1)) 


# Create an SNMP context 
snmpContext = context.SnmpContext(snmpEngine) 

logging.debug('Loading HOST-RESOURCES-MIB module...'), 
mibBuilder = builder.MibBuilder().loadModules('HOST-RESOURCES-MIB') 
logging.debug('done') 

logging.debug('Building MIB tree...'), 
mibInstrum = instrum.MibInstrumController(mibBuilder) 
logging.debug('done') 

logging.debug('Building table entry index from human-friendly representation...') 
# see http://www.oidview.com/mibs/0/HOST-RESOURCES-MIB.html 
hostRunTable, = mibBuilder.importSymbols('HOST-RESOURCES-MIB', 'hrSWRunEntry') 
instanceId = hostRunTable.getInstIdFromIndices(1) 
logging.debug('done') 


# The following shows the OID name mapping 
# 
# hrSWRunTable   1.3.6.1.2.1.25.4.2   <TABLE> 
# hrSWRunEntry   1.3.6.1.2.1.25.4.2.1  <SEQUENCE> 
# hrSWRunIndex   1.3.6.1.2.1.25.4.2.1.1  <Integer32> 
# hrSWRunName   1.3.6.1.2.1.25.4.2.1.2  <InternationalDisplayString> 64 Char 
# hrSWRunID    1.3.6.1.2.1.25.4.2.1.3  <ProductID> 
# hrSWRunPath   1.3.6.1.2.1.25.4.2.1.4  <InternationalDisplayString> 128 octets 
# hrSWRunParameters  1.3.6.1.2.1.25.4.2.1.5  <InternationalDisplayString> 128 octets 
# hrSWRunType   1.3.6.1.2.1.25.4.2.1.6  <INTEGER> 
# hrSWRunStatus   1.3.6.1.2.1.25.4.2.1.7  <INTEGER> <<===== This is the key variable used by Opennms 


# http://docs.opennms.org/opennms/releases/18.0.1/guide-admin/guide-admin.html#_hostresourceswrunmonitor) 


logging.debug('Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:') 
varBinds = mibInstrum.writeVars((
           (hostRunTable.name + (1,) + instanceId, 1), 
           (hostRunTable.name + (2,) + instanceId, 'AppName'), # <=== Must match OpenNMS service-name variable 
           (hostRunTable.name + (3,) + instanceId, {0,0}), # 
           (hostRunTable.name + (4,) + instanceId, 'All is well'), 
           (hostRunTable.name + (5,) + instanceId, 'If this was not the case it would say so here'), 
           (hostRunTable.name + (6,) + instanceId, 4),# Values are ==> unknown(1), operatingSystem(2), deviceDriver(3), application(4)  
           (hostRunTable.name + (7,) + instanceId, 1) #<<=== This is the status number OpenNMS looks at Values are ==> running(1), runnable(2), notRunnable(3), invalid(4) 
           )) 

for oid, val in varBinds: 
    print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint())) 
logging.debug('done') 

logging.debug('Read whole MIB (table walk)') 
oid, val =(), None 
while True: 
    oid, val = mibInstrum.readNextVars(((oid, val),))[0] 
    if exval.endOfMib.isSameTypeWith(val): 
     break 
    print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint())) 
logging.debug('done') 

# logging.debug('Unloading MIB modules...'), 
# mibBuilder.unloadModules() 
# logging.debug('done') 


# --- end of table population --- 

# Register SNMP Applications at the SNMP engine for particular SNMP context 
cmdrsp.GetCommandResponder(snmpEngine, snmpContext) 
cmdrsp.SetCommandResponder(snmpEngine, snmpContext) 
cmdrsp.NextCommandResponder(snmpEngine, snmpContext) 
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext) 

# Register an imaginary never-ending job to keep I/O dispatcher running forever 
snmpEngine.transportDispatcher.jobStarted(1) 

# Run I/O dispatcher which would receive queries and send responses 
try: 
    snmpEngine.transportDispatcher.runDispatcher() 
except: 
    snmpEngine.transportDispatcher.closeDispatcher() 
    raise 

代码运行时不会产生错误。 varBinds和MIB表行走显示我认为我应该期望的:

[2016-12-29 16:42:49,323-INFO]-(SNMPAgent) Starting.... 
[2016-12-29 16:42:49,470-DEBUG]-(SNMPAgent) Loading HOST-RESOURCES-MIB module... 
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done 
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) Building MIB tree... 
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done 
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) Building table entry index from human-friendly representation... 
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done 
[2016-12-29 16:42:49,632-DEBUG]-(SNMPAgent) Create/update HOST-RESOURCES-MIB::hrSWRunTable table row: 
1.3.6.1.2.1.25.4.2.1.1.1 = 1 
[2016-12-29 16:42:49,651-DEBUG]-(SNMPAgent) done 
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader 
1.3.6.1.2.1.25.4.2.1.3.1 = 0 
1.3.6.1.2.1.25.4.2.1.4.1 = All is well 
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here 
1.3.6.1.2.1.25.4.2.1.6.1 = 'application' 
1.3.6.1.2.1.25.4.2.1.7.1 = 'running' 
[2016-12-29 16:42:49,651-DEBUG]-(SNMPAgent) Read whole MIB (table walk) 
1.3.6.1.2.1.25.4.2.1.1.1 = 1 
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader 
1.3.6.1.2.1.25.4.2.1.3.1 = 0 
1.3.6.1.2.1.25.4.2.1.4.1 = All is well 
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here 
1.3.6.1.2.1.25.4.2.1.6.1 = 'application' 
1.3.6.1.2.1.25.4.2.1.7.1 = 'running' 
1.3.6.1.2.1.25.5.1.1.1.1 = <no value> 
1.3.6.1.2.1.25.5.1.1.2.1 = <no value> 
[2016-12-29 16:42:53,490-DEBUG]-(SNMPAgent) done 

最后,启动调度程序。

问题是,当我尝试查询代理没有任何反应。我没有得到任何回应。我查看了我的代码,其中一个显而易见的事实是,我没有明确地将snmpEngine链接到我创建的MIB。我应该这样做吗?

任何洞察力都会得到很大的回响,因为我很难理解目前的状况。

回答

2

我以为我只会发布一个答案,因为我花了这么长时间才弄清楚如何去做我所需要的。希望别人会觉得这很有用。以下代码允许我填充pysnmp已知的任何MIB,然后将MIB作为V2 SNMP代理程序使用。

import logging 

from pysnmp import debug 
from pysnmp.carrier.asyncore.dgram import udp 
from pysnmp.entity import engine, config 
from pysnmp.entity.rfc3413 import cmdrsp, context 
from pysnmp.proto.api import v2c 
from pysnmp.smi import builder, instrum, exval 

# Uncomment this to turn pysnmp debugging on 
#debug.setLogger(debug.Debug('all')) 

formatting = '[%(asctime)s-%(levelname)s]-(%(module)s) %(message)s' 
logging.basicConfig(level=logging.DEBUG, format=formatting,) 

logging.info("Starting....") 

# Create SNMP engine 
snmpEngine = engine.SnmpEngine() 

# Transport setup 

# UDP over IPv4 
config.addTransport(
    snmpEngine, 
    udp.domainName, 
    udp.UdpTransport().openServerMode(('0.0.0.0', 12345)) 
) 

# SNMPv2c setup 

# SecurityName <-> CommunityName mapping. 
config.addV1System(snmpEngine, 'my-area', 'public') 

# Allow read MIB access for this user/securityModels at VACM 
# Limit access to just the custom MIB. Widen if need be 
config.addVacmUser(snmpEngine, 
        2, 
        'my-area', 
        'noAuthNoPriv', 
        (1, 3, 6, 1, 2, 1, 25, 4), 
        (1, 3, 6, 1, 2, 1, 25, 4)) 


# Create an SNMP context and ensure the custom MIB is loaded 
# Your system must have this MIB installed otherwise pysnmp 
# can't load it! 
snmpContext = context.SnmpContext(snmpEngine) 
logging.debug('Loading HOST-RESOURCES-MIB module...'), 
mibBuilder = snmpContext.getMibInstrum().getMibBuilder() 
mibBuilder.loadModules('HOST-RESOURCES-MIB') 
mibInstrum = snmpContext.getMibInstrum() 
logging.debug('done') 

logging.debug('Building table entry index from human-friendly representation...') 
# see http://www.oidview.com/mibs/0/HOST-RESOURCES-MIB.html 
hostRunTable, = mibBuilder.importSymbols('HOST-RESOURCES-MIB', 'hrSWRunEntry') 
instanceId = hostRunTable.getInstIdFromIndices(1) 
logging.debug('done') 


# The following shows the OID name mapping 
# 
# hrSWRunTable   1.3.6.1.2.1.25.4.2   <TABLE> 
# hrSWRunEntry   1.3.6.1.2.1.25.4.2.1  <SEQUENCE> 
# hrSWRunIndex   1.3.6.1.2.1.25.4.2.1.1  <Integer32> 
# hrSWRunName   1.3.6.1.2.1.25.4.2.1.2  <InternationalDisplayString> 64 Char 
# hrSWRunID    1.3.6.1.2.1.25.4.2.1.3  <ProductID> 
# hrSWRunPath   1.3.6.1.2.1.25.4.2.1.4  <InternationalDisplayString> 128 octets 
# hrSWRunParameters  1.3.6.1.2.1.25.4.2.1.5  <InternationalDisplayString> 128 octets 
# hrSWRunType   1.3.6.1.2.1.25.4.2.1.6  <INTEGER> 
# hrSWRunStatus   1.3.6.1.2.1.25.4.2.1.7  <INTEGER> <<===== This is the key variable used by Opennms 

# We are going to use OpenNMS as the SNMP manager. OpenNMS will poll this agent to check on its status. The manual 
# states: 
# 
# "This monitor tests the running state of one or more processes. It does this using SNMP and by inspecting the 
# hrSwRunTable of the HOST-RESOURCES-MIB. The test is done by matching a given process as hrSWRunName against 
# the numeric value of the hrSWRunStatus". hrSWRunName is matched against the process name defined in the OpenNMS 
# config file under the heading "service-name". hrSWRunStatus is set to whatever your desired status is. OpenNMS 
# will compare this value against the config file variable run-level. If hrSWRunStatus > run-level the process 
# will be marked as having problems. for the complete page see: 
# http://docs.opennms.org/opennms/releases/18.0.1/guide-admin/guide-admin.html#_hostresourceswrunmonitor) 


# I have made up an enterprise MIB for us. The number is moot as it's not going to go anywhere but the code needs 
# something valid. 
# The enterprise MIB I have chosen is 
enterpriseMib = (1, 3, 6, 1, 4, 1, 50000, 0) 

logging.debug('Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:') 
varBinds = mibInstrum.writeVars((
           (hostRunTable.name + (1,) + instanceId, 1), 
           (hostRunTable.name + (2,) + instanceId, 'TradeLoader'), # <=== Must match OpenNMS service-name variable 
           (hostRunTable.name + (3,) + instanceId, enterpriseMib), # 
           (hostRunTable.name + (4,) + instanceId, 'All is well'), 
           (hostRunTable.name + (5,) + instanceId, 'If this was not the case it would say so here'), 
           (hostRunTable.name + (6,) + instanceId, 4),# Values are ==> unknown(1), operatingSystem(2), deviceDriver(3), application(4)  
           (hostRunTable.name + (7,) + instanceId, 1) #<<=== This is the status number OpenNMS looks at Values are ==> running(1), runnable(2), notRunnable(3), invalid(4) 
           )) 

# --- end of table population --- 
logging.debug('Confirm that the data has been set by reading whole MIB (table walk)') 
oid, val =(), None 
while True: 
    oid, val = mibInstrum.readNextVars(((oid, val),))[0] 
    if exval.endOfMib.isSameTypeWith(val): 
     break 
    print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint())) 
logging.debug('done') 

# Register SNMP Applications at the SNMP engine for particular SNMP context 
cmdrsp.GetCommandResponder(snmpEngine, snmpContext) 
cmdrsp.SetCommandResponder(snmpEngine, snmpContext) 
cmdrsp.NextCommandResponder(snmpEngine, snmpContext) 
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext) 

# Register an imaginary never-ending job to keep I/O dispatcher running forever 
snmpEngine.transportDispatcher.jobStarted(1) 

# Run I/O dispatcher which would receive queries and send responses 
try: 
    snmpEngine.transportDispatcher.runDispatcher() 
except: 
    snmpEngine.transportDispatcher.closeDispatcher() 
    raise 

记录器的消息显示

[2017-01-09 16:30:15,401-INFO]-(SNMPAgent) Starting.... [2017-01-09 16:30:15,490-DEBUG]-(SNMPAgent) Loading HOST-RESOURCES-MIB module... [2017-01-09 16:30:15,513-DEBUG]-(SNMPAgent) done [2017-01-09 16:30:15,513-DEBUG]-(SNMPAgent) Building table entry index from human-friendly representation... [2017-01-09 16:30:15,515-DEBUG]-(SNMPAgent) done [2017-01-09 16:30:15,515-DEBUG]-(SNMPAgent) Create/update HOST-RESOURCES-MIB::hrSWRunTable table row: [2017-01-09 16:30:15,536-DEBUG]-(SNMPAgent) Confirm that the data has been set by reading whole MIB (table walk) 
1.3.6.1.2.1.25.4.2.1.1.1 = 1 
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader 
1.3.6.1.2.1.25.4.2.1.3.1 = 1.3.6.1.4.1.50000.0 
1.3.6.1.2.1.25.4.2.1.4.1 = All is well 
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here 
1.3.6.1.2.1.25.4.2.1.6.1 = 'application' 
1.3.6.1.2.1.25.4.2.1.7.1 = 'running' 
1.3.6.1.2.1.25.5.1.1.1.1 = <no value> 
1.3.6.1.2.1.25.5.1.1.2.1 = <no value> 
1.3.6.1.6.3.10.2.1.1.0 = 0x80004fb805049c06c8 
1.3.6.1.6.3.10.2.1.2.0 = 2 
1.3.6.1.6.3.10.2.1.3.0 = 0 
1.3.6.1.6.3.10.2.1.4.0 = 65507 
1.3.6.1.6.3.16.1.1.1.1.0 = 
1.3.6.1.6.3.16.1.2.1.1.2.7.109.121.45.97.114.101.97 = 2 
1.3.6.1.6.3.16.1.2.1.2.2.7.109.121.45.97.114.101.97 = my-area 
1.3.6.1.6.3.16.1.2.1.3.2.7.109.121.45.97.114.101.97 = v-1203634843-2 
1.3.6.1.6.3.16.1.2.1.4.2.7.109.121.45.97.114.101.97 = 'nonVolatile' 
1.3.6.1.6.3.16.1.2.1.5.2.7.109.121.45.97.114.101.97 = 'active' 
1.3.6.1.6.3.16.1.4.1.1.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 
1.3.6.1.6.3.16.1.4.1.2.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 2 
1.3.6.1.6.3.16.1.4.1.3.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'noAuthNoPriv' 
1.3.6.1.6.3.16.1.4.1.4.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'exact' 
1.3.6.1.6.3.16.1.4.1.5.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = rv-1203634843-2 
1.3.6.1.6.3.16.1.4.1.6.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = wv-1203634843-2 
1.3.6.1.6.3.16.1.4.1.7.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = nv-1203634843-2 
1.3.6.1.6.3.16.1.4.1.8.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'nonVolatile' 
1.3.6.1.6.3.16.1.4.1.9.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'active' 
1.3.6.1.6.3.16.1.5.2.1.1.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= rv-1203634843-2 
1.3.6.1.6.3.16.1.5.2.1.1.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= wv-1203634843-2 
1.3.6.1.6.3.16.1.5.2.1.2.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 1.3.6.1.2.1.25.4 
1.3.6.1.6.3.16.1.5.2.1.2.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 1.3.6.1.2.1.25.4 
1.3.6.1.6.3.16.1.5.2.1.3.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 
1.3.6.1.6.3.16.1.5.2.1.3.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 
1.3.6.1.6.3.16.1.5.2.1.4.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 'included' 
1.3.6.1.6.3.16.1.5.2.1.4.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 'included' 
1.3.6.1.6.3.16.1.5.2.1.5.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 'nonVolatile' 
1.3.6.1.6.3.16.1.5.2.1.5.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 'nonVolatile' 
1.3.6.1.6.3.16.1.5.2.1.6.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 'active' 
1.3.6.1.6.3.16.1.5.2.1.6.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4 
= 'active' 
1.3.6.1.6.3.18.1.1.1.1.109.121.45.97.114.101.97 = my-area 
1.3.6.1.6.3.18.1.1.1.2.109.121.45.97.114.101.97 = public 
1.3.6.1.6.3.18.1.1.1.3.109.121.45.97.114.101.97 = my-area 
1.3.6.1.6.3.18.1.1.1.4.109.121.45.97.114.101.97 = 0x80004fb805049c06c8 
1.3.6.1.6.3.18.1.1.1.5.109.121.45.97.114.101.97 = 
1.3.6.1.6.3.18.1.1.1.6.109.121.45.97.114.101.97 = 
1.3.6.1.6.3.18.1.1.1.7.109.121.45.97.114.101.97 = 'nonVolatile' 
1.3.6.1.6.3.18.1.1.1.8.109.121.45.97.114.101.97 = 'active' [2017-01-09 16:30:15,683-DEBUG]-(SNMPAgent) done 

和代理可以查询,显示得到的结果如下:

snmpwalk -v 2c -c public -n my-context 0.0.0.0:12345 1.3.6 

HOST-RESOURCES-MIB::hrSWRunIndex.1 = INTEGER: 1 
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "TradeLoader" 
HOST-RESOURCES-MIB::hrSWRunID.1 = OID: SNMPv2-SMI::enterprises.50000.0 
HOST-RESOURCES-MIB::hrSWRunPath.1 = STRING: "All is well" 
HOST-RESOURCES-MIB::hrSWRunParameters.1 = STRING: "If this was not the case it would say so here" 
HOST-RESOURCES-MIB::hrSWRunType.1 = INTEGER: application(4) 
HOST-RESOURCES-MIB::hrSWRunStatus.1 = INTEGER: running(1) 
HOST-RESOURCES-MIB::hrSWRunStatus.1 = No more variables left in this MIB View (It is past the end of the MIB tree) 
1

你说

的问题是,当我尝试和查询代理没有任何反应。我没有得到任何回应

看起来像2个问题:

一)本地主机解析到我的系统上回送地址127.0.0.1,所以你只能在同一个系统的代理访问它。

b)您在addVacmUser注册为SNMP版本2,所以你不能用V1

之后访问:

% snmpwalk -v 2c -c public 127.0.0.1:12345 
SNMPv2-MIB::sysDescr.0 = STRING: PySNMP engine version 4.3.2, Python 2.7.5 (default, Nov 3 2014, 14:33:39) [GCC 4.8.3 20140911 (Red Hat 4.8.3-7)] 
SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises.20408 
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (1) 0:00:00.01 
SNMPv2-MIB::sysContact.0 = STRING: 
... 

虽然没有HOST-RESOURCE-MIB对象。这是另一个问题。

+0

显然,如果你把 “localhost” 为0.0.0.0( INADDR_ANY),那么你将能够从另一个系统访问。 –

+0

感谢您的回复。我使用localhost的原因是因为我正在使用同一台机器上的代理和经理进行测试。我不相信会导致问题,或者我错了吗?你的第二个问题让我有些困惑,因为我不试图用SNMP V1访问代理(我不相信我的例子也是这样) –

+0

嗯,你不会告诉我们你如何“查询”代理。 –