diff options
author | elie <elie> | 2014-11-04 20:21:48 +0000 |
---|---|---|
committer | elie <elie> | 2014-11-04 20:21:48 +0000 |
commit | 963a6d644e4a00ed82adadfc09b535432061f0cd (patch) | |
tree | 31551e113a0b1403515f55947c12fbac57a5f7ff /pysnmp/carrier | |
parent | 08c9b6df27aa45b442e0a799049e33c31ff486da (diff) | |
download | pysnmp-git-963a6d644e4a00ed82adadfc09b535432061f0cd.tar.gz |
initial support for asyncio network transport added
Diffstat (limited to 'pysnmp/carrier')
-rw-r--r-- | pysnmp/carrier/asyncio/__init__.py | 1 | ||||
-rw-r--r-- | pysnmp/carrier/asyncio/base.py | 35 | ||||
-rw-r--r-- | pysnmp/carrier/asyncio/dgram/__init__.py | 1 | ||||
-rw-r--r-- | pysnmp/carrier/asyncio/dgram/base.py | 75 | ||||
-rw-r--r-- | pysnmp/carrier/asyncio/dgram/udp.py | 63 | ||||
-rw-r--r-- | pysnmp/carrier/asyncio/dispatch.py | 78 |
6 files changed, 253 insertions, 0 deletions
diff --git a/pysnmp/carrier/asyncio/__init__.py b/pysnmp/carrier/asyncio/__init__.py new file mode 100644 index 00000000..ac0b2c02 --- /dev/null +++ b/pysnmp/carrier/asyncio/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package.
diff --git a/pysnmp/carrier/asyncio/base.py b/pysnmp/carrier/asyncio/base.py new file mode 100644 index 00000000..192343f9 --- /dev/null +++ b/pysnmp/carrier/asyncio/base.py @@ -0,0 +1,35 @@ +# +# Copyright (C) 2014, Zebra Technologies +# Authors: Matt Hooks <me@matthooks.com> +# Zachary Lorusso <zlorusso@gmail.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +from pysnmp.carrier.asyncio.dispatch import AsyncioDispatcher +from pysnmp.carrier.base import AbstractTransport + +class AbstractAsyncioTransport(AbstractTransport): + protoTransportDispatcher = AsyncioDispatcher + """Base Asyncio Transport, to be used with AsyncioDispatcher""" + def __init__(self): + self._writeQ = [] diff --git a/pysnmp/carrier/asyncio/dgram/__init__.py b/pysnmp/carrier/asyncio/dgram/__init__.py new file mode 100644 index 00000000..ac0b2c02 --- /dev/null +++ b/pysnmp/carrier/asyncio/dgram/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package.
diff --git a/pysnmp/carrier/asyncio/dgram/base.py b/pysnmp/carrier/asyncio/dgram/base.py new file mode 100644 index 00000000..c45e3584 --- /dev/null +++ b/pysnmp/carrier/asyncio/dgram/base.py @@ -0,0 +1,75 @@ +# +# Copyright (C) 2014, Zebra Technologies +# Authors: Matt Hooks <me@matthooks.com> +# Zachary Lorusso <zlorusso@gmail.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +from pysnmp.carrier.asyncio.base import AbstractAsyncioTransport +from pysnmp.carrier import error +from pysnmp import debug +try: + import asyncio +except ImportError: + from pysnmp.error import PySnmpError + raise PySnmpError('The asyncio transport is not available') + +loop = asyncio.get_event_loop() + +class DgramAsyncioProtocol(asyncio.DatagramProtocol, AbstractAsyncioTransport): + """Base Asyncio datagram Transport, to be used with AsyncioDispatcher""" + transport = None + + def datagram_received(self, datagram, transportAddress): + if self._cbFun is None: + raise error.CarrierError('Unable to call cbFun') + else: + loop.call_soon(self._cbFun, self, transportAddress, datagram) + + def connection_made(self, transport): + self.transport = transport + debug.logger & debug.flagIO and debug.logger('connection_made: invoked') + while self._writeQ: + outgoingMessage, transportAddress = self._writeQ.pop(0) + debug.logger & debug.flagIO and debug.logger('connection_made: transportAddress %r outgoingMessage %s' % + (transportAddress, debug.hexdump(outgoingMessage))) + try: + self.transport.sendto(outgoingMessage, transportAddress) + except Exception as err: + raise error.CarrierError() from err + + def connection_lost(self, exc): + debug.logger & debug.flagIO and debug.logger('connection_lost: invoked') + + 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"), + transportAddress, debug.hexdump(outgoingMessage) + )) + if self.transport is None: + self._writeQ.append((outgoingMessage, transportAddress)) + else: + try: + self.transport.sendto(outgoingMessage, transportAddress) + except Exception as err: + raise error.CarrierError() from err diff --git a/pysnmp/carrier/asyncio/dgram/udp.py b/pysnmp/carrier/asyncio/dgram/udp.py new file mode 100644 index 00000000..d29981a4 --- /dev/null +++ b/pysnmp/carrier/asyncio/dgram/udp.py @@ -0,0 +1,63 @@ +# +# Copyright (C) 2014, Zebra Technologies +# Authors: Matt Hooks <me@matthooks.com> +# Zachary Lorusso <zlorusso@gmail.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +from pysnmp.carrier.asyncio.dgram.base import DgramAsyncioProtocol +from pysnmp.carrier import error +try: + import asyncio +except ImportError: + from pysnmp.error import PySnmpError + raise PySnmpError('The asyncio transport is not available') + +loop = asyncio.get_event_loop() + +domainName = snmpUDPDomain = (1, 3, 6, 1, 6, 1, 1) + +class UdpAsyncioTransport(DgramAsyncioProtocol): + # 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 as err: + raise error.CarrierError() from err + 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 as err: + raise error.CarrierError() from err + return self + + def closeTransport(self): + self._lport.cancel() + DgramAsyncioProtocol.closeTransport(self) + +UdpTransport = UdpAsyncioTransport diff --git a/pysnmp/carrier/asyncio/dispatch.py b/pysnmp/carrier/asyncio/dispatch.py new file mode 100644 index 00000000..7b0b72fc --- /dev/null +++ b/pysnmp/carrier/asyncio/dispatch.py @@ -0,0 +1,78 @@ +# +# Copyright (C) 2014, Zebra Technologies +# Authors: Matt Hooks <me@matthooks.com> +# Zachary Lorusso <zlorusso@gmail.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +from pysnmp.carrier.base import AbstractTransportDispatcher +try: + import asyncio +except ImportError: + from pysnmp.error import PySnmpError + raise PySnmpError('The asyncio transport is not available') + +loop = asyncio.get_event_loop() + +class AsyncioDispatcher(AbstractTransportDispatcher): + """AsyncioDispatcher based on asyncio event loop""" + def __init__(self, *args, **kwargs): + AbstractTransportDispatcher.__init__(self) + self.__transportCount = 0 + if 'timeout' in kwargs: + self.setTimerResolution(kwargs['timeout']) + self.loopingcall = None + + @asyncio.coroutine + def handle_timeout(self): + while True: + yield from asyncio.sleep(self.getTimerResolution()) + self.handleTimerTick(loop.time()) + + def runDispatcher(self, timeout=0.0): + if not loop.is_running(): + try: + loop.run_forever() + except KeyboardInterrupt: + raise + except Exception as err: + raise PySnmpError('event loop error') from err + + def registerTransport(self, tDomain, transport): + if self.loopingcall is None and self.getTimerResolution() > 0: + self.loopingcall = asyncio.async(self.handle_timeout()) + AbstractTransportDispatcher.registerTransport( + self, tDomain, transport + ) + self.__transportCount = self.__transportCount + 1 + + def unregisterTransport(self, tDomain): + t = AbstractTransportDispatcher.getTransport(self, tDomain) + if t is not None: + AbstractTransportDispatcher.unregisterTransport(self, tDomain) + self.__transportCount = self.__transportCount - 1 + + # The last transport has been removed, stop the timeout + if self.__transportCount == 0 and not self.loopingcall.done(): + self.loopingcall.cancel() + self.loopingcall = None |