summaryrefslogtreecommitdiff
path: root/pysnmp/carrier/asyncio/dgram/base.py
blob: a845fa1e948d91c6b2b15f2a432da4693575c932 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#
# This file is part of pysnmp software.
#
# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
# License: http://snmplabs.com/pysnmp/license.html
#
# 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.
#
import sys
import platform
import traceback
from pysnmp.carrier.asyncio.base import AbstractAsyncioTransport
from pysnmp.carrier import error
from pysnmp import debug

try:
    import asyncio
except ImportError:
    import trollius as asyncio

IS_PYTHON_344_PLUS = platform.python_version_tuple() >= ('3', '4', '4')


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, sock=None, sockMap=None, loop=None):
        self._writeQ = []
        self._lport = None
        if loop is None:
            loop = asyncio.get_event_loop()
        self.loop = loop

    def datagram_received(self, datagram, transportAddress):
        if self._cbFun is None:
            raise error.CarrierError('Unable to call cbFun')
        else:
            self.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, 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 = self.loop.create_datagram_endpoint(
                lambda: self, local_addr=iface, family=self.sockFamily
            )
            # Avoid deprecation warning for asyncio.async()
            if IS_PYTHON_344_PLUS:
              self._lport = asyncio.ensure_future(c)
            else: # pragma: no cover
              self._lport = getattr(asyncio, 'async')(c)

        except Exception:
            raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
        return self

    def openServerMode(self, iface):
        try:
            c = self.loop.create_datagram_endpoint(
                lambda: self, local_addr=iface, family=self.sockFamily
            )
            # Avoid deprecation warning for asyncio.async()
            if IS_PYTHON_344_PLUS:
              self._lport = asyncio.ensure_future(c)
            else: # pragma: no cover
              self._lport = getattr(asyncio, 'async')(c)
        except Exception:
            raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))
        return self

    def closeTransport(self):
        if self._lport is not None:
            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"),
            transportAddress, debug.hexdump(outgoingMessage)
        ))
        if self.transport is None:
            self._writeQ.append((outgoingMessage, transportAddress))
        else:
            try:
                self.transport.sendto(outgoingMessage, self.normalizeAddress(transportAddress))
            except Exception:
                raise error.CarrierError(';'.join(traceback.format_exception(*sys.exc_info())))

    def normalizeAddress(self, transportAddress):
        if not isinstance(transportAddress, self.addressType):
            transportAddress = self.addressType(transportAddress)
        return transportAddress