Bug#656137: readme.txt: asynchronous usage is a misleading lie

Enrico Zini enrico at debian.org
Thu Jan 19 12:05:54 UTC 2012


On Wed, Jan 18, 2012 at 12:55:49AM +0100, Enrico Zini wrote:

> That being done (and I might continue to post the solutions I came up
> with, unless you'd rather I didn't use the BTS in that way)

This is an update with my attempts at hacking / monkeypatching things.

I tried to set a timeout on the socket that Manager uses to connect to
asterisk, but unfortunately BaseManager does not keep a reference to the
socket object it creates, nor it can be found inside the result of
sock.makefile.

The socket integer file descriptor is reachable, but it cannot be
wrapped again into a socket object to call settimeout on it. Doing that
would anyway leave the possibility to get stuck on an unlimited timeout
during _authenticate.

socket.fromfd's documentation states that it duplicates the file
descriptor. I tried using that to set a timeout on the original socket,
but any socket operations after it raise "error: [Errno 11] Resource
temporarily unavailable". Possibly that's because the original socket
has already been used during _authenticate.

I had success with this CoreManager wrapper:

class AsteriskManagerWithTimeout(Asterisk.Manager.CoreManager):
    #def __init__(self, timeout, *args, **kw):
    def __init__(self, timeout, address, username, secret, listen_events = True):
        """
        timeout is a timeout in seconds to be applied to the socket.

        If an action times out, the sock.timeout exception is raised and then
        the manager is left in an unspecified state.

        This has been written to hack the internals of PyAsterisk version
        0.1a3+r160 and may have unpredictable effects on any other version of
        PyAsterisk.
        """
        # We do NOT call the CoreManager.__init__, but we copypaste it here,
        # adding the settimeout call. There seems to be no other way to do it,
        # since we cannot access the socket created by the parent constructor,
        # and sockets created with socket.fromfd raise
        # "error: [Errno 11] Resource temporarily unavailable" when used
        #super(AsteriskManagerWithTimeout, self).__init__(*args, **kw)

        self.address = address
        self.username = username
        self.secret = secret
        self.listen_events = listen_events
        self.events = Asterisk.Util.EventCollection()

        # Configure logging:
        self.log = self.getLogger()
        self.log.debug('Initialising.')

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # The following settimeout call is the only change to the original
        # CoreManager __init__ code
        sock.settimeout(timeout)
        sock.connect(address)

        self.file = sock.makefile('r+', 0) # line buffered.
        self.fileno = self.file.fileno

        self.response_buffer = []
        self._authenticate()

These two functions can be added to have a read loop, instead of
BaseManager.serve_forever, with periodic pings to see if Asterisk is
still alive/reachable:

    def read_loop(self, idle_timeout=5*60.0):
        """
        Read loop that will quit if there has been no input from asterisk for a
        configurable amount of seconds.
        """
        while True:
            rlist, wlist, xlist = select.select([self], [], [], idle_timeout)
            if not rlist:
                # It timed out
                return
            else:
                self.read()

    def read_loop_with_pings(self, idle_timeout=5*60.0):
        """
        Read loop that, if nothing is heard from Asterisk after a given idle
        timeout, performs a ping to see if Asterks is still there.
        """
        while True:
            self.read_loop(idle_timeout)
            log.info("no news from asterisk after %f seconds: pinging", idle_timeout)
            event = self.Ping()
            log.info("asterisk replied to ping")


Finally, this is my current prototype for the Manager supervisor:

    def persistent_asterisk_connection(host, port, user, password):
        timeout = 60
        idle_timeout = 30
        try:
            while True:
                try:
                    log.info("connecting to asterisk manager %s:%d as user %s...",
                            host, port, user)
                    manager = AsteriskManagerWithTimeout(timeout, (host, port), user, password)
                    log.info("connection to asterisk manager %s:%d succeeded.", host, port)
                    manager.read_loop_with_pings(idle_timeout)
                except Asterisk.Manager.GoneAwayError, e:
                    log.error("asterisk has gone away: %s", str(e))
                except socket.timeout, e:
                    log.error("asterisk socket timeout")
                except socket.error, e:
                    log.error("asterisk socket error: %s", str(e))

		# FIXME: you could be less aggressive and have this
		# increment over time
                time.sleep(1)
        except Exception, e:
            log.error("process terminating because of unsupported exception: %s", str(e))
        except KeyboardInterrupt:
            log.warn("keyboard interrupt: quitting")


Ciao,

Enrico

-- 
GPG key: 4096R/E7AD5568 2009-05-08 Enrico Zini <enrico at enricozini.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <http://lists.alioth.debian.org/pipermail/pkg-voip-maintainers/attachments/20120119/135868da/attachment.pgp>


More information about the Pkg-voip-maintainers mailing list