summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelie <elie>2015-03-08 08:00:33 +0000
committerelie <elie>2015-03-08 08:00:33 +0000
commit6c5f2dd6f3d4ad6b839a696af58211d47a901730 (patch)
tree6478f5b001bf5fa635d5ad9c02ae67eaa19e9e09
parent1bb9c7913639f9d839b8c2b07577efeb0b1e9bac (diff)
downloadpysnmp-6c5f2dd6f3d4ad6b839a696af58211d47a901730.tar.gz
UDP/IPv6 support added to asyncio-based transport
-rw-r--r--CHANGES3
-rw-r--r--examples/v3arch/asyncio/manager/cmdgen/get-v2c-over-ipv6.py96
-rw-r--r--pysnmp/carrier/asyncio/dgram/base.py43
-rw-r--r--pysnmp/carrier/asyncio/dgram/udp.py26
-rw-r--r--pysnmp/carrier/asyncio/dgram/udp6.py33
5 files changed, 169 insertions, 32 deletions
diff --git a/CHANGES b/CHANGES
index 06043a1..d61cac3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,7 +21,8 @@ Revision 4.2.6rc2
getting peer's transport endpoint which is not suggested in RFCs)
will be gradually migrated to this new framework.
- Initial support for the asyncio & Trollius frameworks and
- coroutines-based SNMP Applications interfaces added.
+ coroutines-based SNMP Applications interfaces added. Both IPv4 and IPv6
+ datagram transports are currently supported.
- The asyncore-based transport subsystem extended to support POSIX
sendmsg()/recvmsg() based socket communication what could be used,
among other things, in the context of a transparent SNMP proxy
diff --git a/examples/v3arch/asyncio/manager/cmdgen/get-v2c-over-ipv6.py b/examples/v3arch/asyncio/manager/cmdgen/get-v2c-over-ipv6.py
new file mode 100644
index 0000000..e1b3171
--- /dev/null
+++ b/examples/v3arch/asyncio/manager/cmdgen/get-v2c-over-ipv6.py
@@ -0,0 +1,96 @@
+#
+# GET Command Generator
+#
+# Send a SNMP GET request
+# with SNMPv2c, community 'public'
+# using Asyncio framework for network transport
+# over IPv6/UDP
+# to an Agent at [::]:161
+# for an OID in string form
+#
+# This script performs similar to the following Net-SNMP command:
+#
+# $ snmpget -v2c -c public -ObentU udp6:[::1]:161 1.3.6.1.2.1.1.1.0
+#
+# Requires Python 3.4 and later!
+#
+from pysnmp.carrier.asyncio.dgram import udp6
+from pysnmp.entity.rfc3413.asyncio import cmdgen
+from pysnmp.entity import engine, config
+import asyncio
+
+# Get the event loop for this thread
+loop = asyncio.get_event_loop()
+
+# Create SNMP engine instance
+snmpEngine = engine.SnmpEngine()
+
+#
+# SNMPv2c setup
+#
+
+# SecurityName <-> CommunityName mapping
+config.addV1System(snmpEngine, 'my-area', 'public')
+
+# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
+config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
+
+#
+# Setup transport endpoint and bind it with security settings yielding
+# a target name
+#
+
+# UDP/IPv6
+config.addTransport(
+ snmpEngine,
+ udp6.domainName,
+ udp6.Udp6Transport().openClientMode()
+)
+config.addTargetAddr(
+ snmpEngine, 'my-router',
+ udp6.domainName, ('::1', 161),
+ 'my-creds'
+)
+
+@asyncio.coroutine
+def snmpOperation(snmpEngine, target, contextEngineId, contextName, varBinds):
+ ( snmpEngine,
+ errorIndication,
+ errorStatus,
+ errorIndex,
+ varBinds ) = yield from cmdgen.GetCommandGenerator().sendVarBinds(
+ snmpEngine,
+ target,
+ contextEngineId,
+ contextName,
+ varBinds
+ )
+
+ if errorIndication:
+ print(errorIndication)
+ elif errorStatus:
+ print('%s at %s' % (
+ errorStatus.prettyPrint(),
+ errorIndex and varBinds[int(errorIndex)-1][0] or '?'
+ )
+ )
+ else:
+ for oid, val in varBinds:
+ print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
+
+ # This also terminates internal timer
+ config.delTransport(
+ snmpEngine,
+ udp6.domainName
+ ).closeTransport()
+
+loop.run_until_complete(
+ snmpOperation(
+ snmpEngine,
+ 'my-router',
+ None, '', # contextEngineId, contextName
+ ( ('1.3.6.1.2.1.1.1.0', None), )
+ )
+)
+
+loop.close()
diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py
index 25b64a2..a37aa26 100644
--- a/pysnmp/carrier/asyncio/dgram/base.py
+++ b/pysnmp/carrier/asyncio/dgram/base.py
@@ -39,6 +39,8 @@ loop = asyncio.get_event_loop()
class DgramAsyncioProtocol(asyncio.DatagramProtocol, AbstractAsyncioTransport):
"""Base Asyncio datagram Transport, to be used with AsyncioDispatcher"""
+ sockFamily = None
+ addressType = lambda x: x
transport = None
def __init__(self, *args, **kwargs):
@@ -58,13 +60,41 @@ class DgramAsyncioProtocol(asyncio.DatagramProtocol, AbstractAsyncioTransport):
debug.logger & debug.flagIO and debug.logger('connection_made: transportAddress %r outgoingMessage %s' %
(transportAddress, debug.hexdump(outgoingMessage)))
try:
- self.transport.sendto(outgoingMessage, transportAddress)
+ self.transport.sendto(outgoingMessage, self.normalizeAddress(transportAddress))
except Exception:
raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
def connection_lost(self, exc):
debug.logger & debug.flagIO and debug.logger('connection_lost: invoked')
+ # AbstractAsyncioTransport API
+
+ def openClientMode(self, iface=None):
+ try:
+ c = loop.create_datagram_endpoint(
+ lambda: self, local_addr=iface, family=self.sockFamily
+ )
+ self._lport = asyncio.async(c)
+ except Exception:
+ raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
+ return self
+
+ def openServerMode(self, iface):
+ try:
+ c = loop.create_datagram_endpoint(
+ lambda: self, local_addr=iface, family=self.sockFamily
+ )
+ self._lport = asyncio.async(c)
+ except Exception:
+ raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
+ return self
+
+ def closeTransport(self):
+ self._lport.cancel()
+ if self.transport is not None:
+ self.transport.close()
+ AbstractAsyncioTransport.closeTransport(self)
+
def sendMessage(self, outgoingMessage, transportAddress):
debug.logger & debug.flagIO and debug.logger('sendMessage: %s transportAddress %r outgoingMessage %s' % (
(self.transport is None and "queuing" or "sending"),
@@ -74,12 +104,11 @@ class DgramAsyncioProtocol(asyncio.DatagramProtocol, AbstractAsyncioTransport):
self._writeQ.append((outgoingMessage, transportAddress))
else:
try:
- self.transport.sendto(outgoingMessage, transportAddress)
+ self.transport.sendto(outgoingMessage, self.normalizeAddress(transportAddress))
except Exception:
raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
- def closeTransport(self):
- if self.transport is not None:
- self.transport.close()
- AbstractAsyncioTransport.closeTransport(self)
-
+ def normalizeAddress(self, transportAddress):
+ if not isinstance(transportAddress, self.addressType):
+ transportAddress = self.addressType(transportAddress)
+ return transportAddress
diff --git a/pysnmp/carrier/asyncio/dgram/udp.py b/pysnmp/carrier/asyncio/dgram/udp.py
index 328423b..653c83c 100644
--- a/pysnmp/carrier/asyncio/dgram/udp.py
+++ b/pysnmp/carrier/asyncio/dgram/udp.py
@@ -25,8 +25,7 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
-import sys
-import traceback
+import socket
from pysnmp.carrier.base import AbstractTransportAddress
from pysnmp.carrier.asyncio.dgram.base import DgramAsyncioProtocol
from pysnmp.carrier import error
@@ -42,28 +41,7 @@ domainName = snmpUDPDomain = (1, 3, 6, 1, 6, 1, 1)
class UdpTransportAddress(tuple, AbstractTransportAddress): pass
class UdpAsyncioTransport(DgramAsyncioProtocol):
+ sockFamily = socket.AF_INET
addressType = UdpTransportAddress
- # AbstractAsyncioTransport API
-
- def openClientMode(self, iface=('0.0.0.0', 0)):
- try:
- c = loop.create_datagram_endpoint(lambda: self, local_addr=iface)
- self._lport = asyncio.async(c)
- except Exception:
- raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
- return self
-
- def openServerMode(self, iface=('0.0.0.0', 161)):
- try:
- c = loop.create_datagram_endpoint(lambda: self, local_addr=iface)
- self._lport = asyncio.async(c)
- except Exception:
- raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
- return self
-
- def closeTransport(self):
- self._lport.cancel()
- DgramAsyncioProtocol.closeTransport(self)
-
UdpTransport = UdpAsyncioTransport
diff --git a/pysnmp/carrier/asyncio/dgram/udp6.py b/pysnmp/carrier/asyncio/dgram/udp6.py
new file mode 100644
index 0000000..8d79174
--- /dev/null
+++ b/pysnmp/carrier/asyncio/dgram/udp6.py
@@ -0,0 +1,33 @@
+import socket
+from pysnmp.carrier.base import AbstractTransportAddress
+from pysnmp.carrier.asyncio.dgram.base import DgramAsyncioProtocol
+from pysnmp.carrier import error
+try:
+ import asyncio
+except ImportError:
+ import trollius as asyncio
+
+loop = asyncio.get_event_loop()
+
+domainName = snmpUDP6Domain = (1, 3, 6, 1, 2, 1, 100, 1, 2)
+
+class Udp6TransportAddress(tuple, AbstractTransportAddress): pass
+
+class Udp6AsyncioTransport(DgramAsyncioProtocol):
+ sockFamily = socket.has_ipv6 and socket.AF_INET6 or None
+ addressType = Udp6TransportAddress
+
+ def normalizeAddress(self, transportAddress):
+ if '%' in transportAddress[0]: # strip zone ID
+ return self.addressType(
+ (transportAddress[0].split('%')[0],
+ transportAddress[1],
+ 0, # flowinfo
+ 0) # scopeid
+ )
+ else:
+ return self.addressType(
+ (transportAddress[0], transportAddress[1], 0, 0)
+ )
+
+Udp6Transport = Udp6AsyncioTransport