From 6c5f2dd6f3d4ad6b839a696af58211d47a901730 Mon Sep 17 00:00:00 2001 From: elie Date: Sun, 8 Mar 2015 08:00:33 +0000 Subject: UDP/IPv6 support added to asyncio-based transport --- CHANGES | 3 +- .../asyncio/manager/cmdgen/get-v2c-over-ipv6.py | 96 ++++++++++++++++++++++ pysnmp/carrier/asyncio/dgram/base.py | 43 ++++++++-- pysnmp/carrier/asyncio/dgram/udp.py | 26 +----- pysnmp/carrier/asyncio/dgram/udp6.py | 33 ++++++++ 5 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 examples/v3arch/asyncio/manager/cmdgen/get-v2c-over-ipv6.py create mode 100644 pysnmp/carrier/asyncio/dgram/udp6.py 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 -- cgit v1.2.1