2010-06-17 20:16:28 +04:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# vim: set expandtab shiftwidth=4:
|
|
|
|
|
|
|
|
"""
|
|
|
|
Python Interface for Asterisk Manager
|
|
|
|
|
|
|
|
This module provides a Python API for interfacing with the asterisk manager.
|
|
|
|
|
|
|
|
import asterisk.manager
|
|
|
|
import sys
|
|
|
|
|
|
|
|
def handle_shutdown(event, manager):
|
|
|
|
print "Recieved shutdown event"
|
|
|
|
manager.close()
|
|
|
|
# we could analize the event and reconnect here
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def handle_event(event, manager):
|
|
|
|
print "Recieved event: %s" % event.name
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
manager = asterisk.manager.Manager()
|
|
|
|
try:
|
|
|
|
# connect to the manager
|
|
|
|
try:
|
2011-05-31 23:56:57 +04:00
|
|
|
manager.connect('host')
|
2010-06-17 20:16:28 +04:00
|
|
|
manager.login('user', 'secret')
|
|
|
|
|
|
|
|
# register some callbacks
|
|
|
|
manager.register_event('Shutdown', handle_shutdown) # shutdown
|
|
|
|
manager.register_event('*', handle_event) # catch all
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# get a status report
|
|
|
|
response = manager.status()
|
|
|
|
|
|
|
|
manager.logoff()
|
2014-10-07 22:49:54 +04:00
|
|
|
except asterisk.manager.ManagerSocketException as e:
|
|
|
|
print "Error connecting to the manager: %s" % e.strerror
|
2010-06-17 20:16:28 +04:00
|
|
|
sys.exit(1)
|
2014-10-07 22:49:54 +04:00
|
|
|
except asterisk.manager.ManagerAuthException as e:
|
|
|
|
print "Error logging in to the manager: %s" % e.strerror
|
2010-06-17 20:16:28 +04:00
|
|
|
sys.exit(1)
|
2014-10-07 22:49:54 +04:00
|
|
|
except asterisk.manager.ManagerException as e:
|
|
|
|
print "Error: %s" % e.strerror
|
2010-06-17 20:16:28 +04:00
|
|
|
sys.exit(1)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
finally:
|
|
|
|
# remember to clean up
|
|
|
|
manager.close()
|
|
|
|
|
|
|
|
Remember all header, response, and event names are case sensitive.
|
|
|
|
|
|
|
|
Not all manager actions are implmented as of yet, feel free to add them
|
|
|
|
and submit patches.
|
|
|
|
"""
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
import sys
|
|
|
|
import os
|
2010-06-17 20:16:28 +04:00
|
|
|
import socket
|
|
|
|
import threading
|
2015-02-26 22:10:32 +03:00
|
|
|
from six import PY3
|
2014-10-07 22:49:54 +04:00
|
|
|
from six.moves import queue
|
2010-06-17 20:16:28 +04:00
|
|
|
import re
|
|
|
|
from types import *
|
|
|
|
from time import sleep
|
|
|
|
|
|
|
|
EOL = '\r\n'
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
|
2011-05-31 23:56:57 +04:00
|
|
|
class ManagerMsg(object):
|
2010-06-17 20:16:28 +04:00
|
|
|
"""A manager interface message"""
|
|
|
|
def __init__(self, response):
|
2010-06-18 23:27:20 +04:00
|
|
|
# the raw response, straight from the horse's mouth:
|
|
|
|
self.response = response
|
2010-06-17 20:16:28 +04:00
|
|
|
self.data = ''
|
|
|
|
self.headers = {}
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# parse the response
|
|
|
|
self.parse(response)
|
|
|
|
|
2010-06-18 23:27:20 +04:00
|
|
|
# This is an unknown message, may happen if a command (notably
|
|
|
|
# 'dialplan show something') contains a \n\r\n sequence in the
|
|
|
|
# middle of output. We hope this happens only *once* during a
|
|
|
|
# misbehaved command *and* the command ends with --END COMMAND--
|
|
|
|
# in that case we return an Event. Otherwise we asume it is
|
|
|
|
# from a misbehaving command not returning a proper header (e.g.
|
|
|
|
# IAXnetstats in Asterisk 1.4.X)
|
|
|
|
# A better solution is probably to retain some knowledge of
|
|
|
|
# commands sent and their expected return syntax. In that case
|
|
|
|
# we could wait for --END COMMAND-- for 'command'.
|
|
|
|
# B0rken in asterisk. This should be parseable without context.
|
|
|
|
if 'Event' not in self.headers and 'Response' not in self.headers:
|
|
|
|
# there are commands that return the ActionID but not
|
|
|
|
# 'Response', e.g., IAXpeers in Asterisk 1.4.X
|
|
|
|
if self.has_header('ActionID'):
|
|
|
|
self.headers['Response'] = 'Generated Header'
|
|
|
|
elif '--END COMMAND--' in self.data:
|
|
|
|
self.headers['Event'] = 'NoClue'
|
|
|
|
else:
|
|
|
|
self.headers['Response'] = 'Generated Header'
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def parse(self, response):
|
|
|
|
"""Parse a manager message"""
|
|
|
|
|
|
|
|
data = []
|
2012-11-11 21:43:41 +04:00
|
|
|
for n, line in enumerate(response):
|
2010-06-18 12:31:10 +04:00
|
|
|
# all valid header lines end in \r\n
|
2012-11-11 21:43:41 +04:00
|
|
|
if not line.endswith('\r\n'):
|
2010-06-18 12:31:10 +04:00
|
|
|
data.extend(response[n:])
|
|
|
|
break
|
|
|
|
try:
|
2012-11-11 21:43:41 +04:00
|
|
|
k, v = (x.strip() for x in line.split(':', 1))
|
2010-06-18 12:31:10 +04:00
|
|
|
self.headers[k] = v
|
|
|
|
except ValueError:
|
|
|
|
# invalid header, start of multi-line data response
|
|
|
|
data.extend(response[n:])
|
|
|
|
break
|
|
|
|
self.data = ''.join(data)
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
def has_header(self, hname):
|
|
|
|
"""Check for a header"""
|
2012-11-11 21:43:41 +04:00
|
|
|
return hname in self.headers
|
2010-06-17 20:16:28 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
def get_header(self, hname, defval=None):
|
2010-06-17 20:16:28 +04:00
|
|
|
"""Return the specfied header"""
|
2010-06-23 11:44:16 +04:00
|
|
|
return self.headers.get(hname, defval)
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
def __getitem__(self, hname):
|
|
|
|
"""Return the specfied header"""
|
|
|
|
return self.headers[hname]
|
2012-08-26 16:30:34 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def __repr__(self):
|
2012-08-26 16:30:34 +04:00
|
|
|
if 'Response' in self.headers:
|
|
|
|
return self.headers['Response']
|
|
|
|
else:
|
|
|
|
return self.headers['Event']
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
class Event(object):
|
|
|
|
"""Manager interface Events, __init__ expects and 'Event' message"""
|
|
|
|
def __init__(self, message):
|
|
|
|
|
|
|
|
# store all of the event data
|
|
|
|
self.message = message
|
|
|
|
self.data = message.data
|
|
|
|
self.headers = message.headers
|
|
|
|
|
|
|
|
# if this is not an event message we have a problem
|
|
|
|
if not message.has_header('Event'):
|
2012-11-11 21:43:41 +04:00
|
|
|
raise ManagerException(
|
|
|
|
'Trying to create event from non event message')
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
# get the event name
|
|
|
|
self.name = message.get_header('Event')
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def has_header(self, hname):
|
|
|
|
"""Check for a header"""
|
2012-11-11 21:43:41 +04:00
|
|
|
return hname in self.headers
|
2010-06-17 20:16:28 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
def get_header(self, hname, defval=None):
|
2010-06-17 20:16:28 +04:00
|
|
|
"""Return the specfied header"""
|
2010-06-23 11:44:16 +04:00
|
|
|
return self.headers.get(hname, defval)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def __getitem__(self, hname):
|
|
|
|
"""Return the specfied header"""
|
|
|
|
return self.headers[hname]
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def __repr__(self):
|
|
|
|
return self.headers['Event']
|
|
|
|
|
|
|
|
def get_action_id(self):
|
2012-11-11 21:43:41 +04:00
|
|
|
return self.headers.get('ActionID', 0000)
|
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
class Manager(object):
|
|
|
|
def __init__(self):
|
|
|
|
self._sock = None # our socket
|
2010-06-17 21:29:23 +04:00
|
|
|
self.title = None # set by received greeting
|
2010-06-17 20:16:28 +04:00
|
|
|
self._connected = threading.Event()
|
|
|
|
self._running = threading.Event()
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# our hostname
|
|
|
|
self.hostname = socket.gethostname()
|
|
|
|
|
|
|
|
# our queues
|
2014-10-07 22:49:54 +04:00
|
|
|
self._message_queue = queue.Queue()
|
|
|
|
self._response_queue = queue.Queue()
|
|
|
|
self._event_queue = queue.Queue()
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
# callbacks for events
|
|
|
|
self._event_callbacks = {}
|
|
|
|
|
|
|
|
self._reswaiting = [] # who is waiting for a response
|
|
|
|
|
|
|
|
# sequence stuff
|
|
|
|
self._seqlock = threading.Lock()
|
|
|
|
self._seq = 0
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# some threads
|
|
|
|
self.message_thread = threading.Thread(target=self.message_loop)
|
2012-11-11 21:43:41 +04:00
|
|
|
self.event_dispatch_thread = threading.Thread(
|
|
|
|
target=self.event_dispatch)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
self.message_thread.setDaemon(True)
|
|
|
|
self.event_dispatch_thread.setDaemon(True)
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
def connected(self):
|
|
|
|
"""
|
|
|
|
Check if we are connected or not.
|
|
|
|
"""
|
|
|
|
return self._connected.isSet()
|
|
|
|
|
|
|
|
def next_seq(self):
|
|
|
|
"""Return the next number in the sequence, this is used for ActionID"""
|
|
|
|
self._seqlock.acquire()
|
|
|
|
try:
|
|
|
|
return self._seq
|
|
|
|
finally:
|
|
|
|
self._seq += 1
|
|
|
|
self._seqlock.release()
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def send_action(self, cdict={}, **kwargs):
|
|
|
|
"""
|
|
|
|
Send a command to the manager
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
If a list is passed to the cdict argument, each item in the list will
|
|
|
|
be sent to asterisk under the same header in the following manner:
|
|
|
|
|
|
|
|
cdict = {"Action": "Originate",
|
|
|
|
"Variable": ["var1=value", "var2=value"]}
|
|
|
|
send_action(cdict)
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
Action: Originate
|
|
|
|
Variable: var1=value
|
|
|
|
Variable: var2=value
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not self._connected.isSet():
|
|
|
|
raise ManagerException("Not connected")
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# fill in our args
|
|
|
|
cdict.update(kwargs)
|
|
|
|
|
|
|
|
# set the action id
|
2012-11-11 21:43:41 +04:00
|
|
|
if 'ActionID' not in cdict:
|
|
|
|
cdict['ActionID'] = '%s-%08x' % (self.hostname, self.next_seq())
|
2010-06-17 20:16:28 +04:00
|
|
|
clist = []
|
|
|
|
|
|
|
|
# generate the command
|
|
|
|
for key, value in cdict.items():
|
|
|
|
if isinstance(value, list):
|
2012-11-11 21:43:41 +04:00
|
|
|
for item in value:
|
|
|
|
item = tuple([key, item])
|
|
|
|
clist.append('%s: %s' % item)
|
2010-06-17 20:16:28 +04:00
|
|
|
else:
|
2012-11-11 21:43:41 +04:00
|
|
|
item = tuple([key, value])
|
|
|
|
clist.append('%s: %s' % item)
|
2010-06-17 20:16:28 +04:00
|
|
|
clist.append(EOL)
|
|
|
|
command = EOL.join(clist)
|
|
|
|
|
2010-06-18 12:31:10 +04:00
|
|
|
# lock the socket and send our command
|
2010-06-17 20:16:28 +04:00
|
|
|
try:
|
2015-02-26 22:10:32 +03:00
|
|
|
self._sock.write(command.encode('ascii'))
|
2010-06-18 12:31:10 +04:00
|
|
|
self._sock.flush()
|
2014-10-07 22:49:54 +04:00
|
|
|
except socket.error as e:
|
|
|
|
raise ManagerSocketException(e.errno, e.strerror)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
self._reswaiting.insert(0, 1)
|
2010-06-17 20:16:28 +04:00
|
|
|
response = self._response_queue.get()
|
|
|
|
self._reswaiting.pop(0)
|
|
|
|
|
|
|
|
if not response:
|
|
|
|
raise ManagerSocketException(0, 'Connection Terminated')
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
def _receive_data(self):
|
|
|
|
"""
|
|
|
|
Read the response from a command.
|
|
|
|
"""
|
|
|
|
|
2010-06-18 12:31:10 +04:00
|
|
|
multiline = False
|
2011-06-01 04:12:23 +04:00
|
|
|
status = False
|
2010-06-18 23:27:20 +04:00
|
|
|
wait_for_marker = False
|
|
|
|
eolcount = 0
|
2010-06-17 20:16:28 +04:00
|
|
|
# loop while we are sill running and connected
|
|
|
|
while self._running.isSet() and self._connected.isSet():
|
|
|
|
try:
|
2010-06-18 12:31:10 +04:00
|
|
|
lines = []
|
2012-11-11 21:43:41 +04:00
|
|
|
for line in self._sock:
|
2015-02-26 22:10:32 +03:00
|
|
|
line = line.decode('ascii')
|
2011-05-31 23:56:57 +04:00
|
|
|
# check to see if this is the greeting line
|
2010-06-18 12:31:10 +04:00
|
|
|
if not self.title and '/' in line and not ':' in line:
|
|
|
|
# store the title of the manager we are connecting to:
|
|
|
|
self.title = line.split('/')[0].strip()
|
|
|
|
# store the version of the manager we are connecting to:
|
|
|
|
self.version = line.split('/')[1].strip()
|
|
|
|
# fake message header
|
2012-11-11 21:43:41 +04:00
|
|
|
lines.append('Response: Generated Header\r\n')
|
|
|
|
lines.append(line)
|
2010-06-18 12:31:10 +04:00
|
|
|
break
|
2010-06-18 23:27:20 +04:00
|
|
|
# If the line is EOL marker we have a complete message.
|
|
|
|
# Some commands are broken and contain a \n\r\n
|
|
|
|
# sequence, in the case wait_for_marker is set, we
|
|
|
|
# have such a command where the data ends with the
|
|
|
|
# marker --END COMMAND--, so we ignore embedded
|
|
|
|
# newlines until we see that marker
|
2012-11-11 21:43:41 +04:00
|
|
|
if line == EOL and not wait_for_marker:
|
2010-06-18 23:27:20 +04:00
|
|
|
multiline = False
|
2010-06-18 12:31:10 +04:00
|
|
|
if lines or not self._connected.isSet():
|
2010-06-17 20:16:28 +04:00
|
|
|
break
|
2010-06-18 12:31:10 +04:00
|
|
|
# ignore empty lines at start
|
|
|
|
continue
|
2011-06-01 04:12:23 +04:00
|
|
|
# If the user executed the status command, it's a special
|
|
|
|
# case, so we need to look for a marker.
|
|
|
|
if 'status will follow' in line:
|
|
|
|
status = True
|
|
|
|
wait_for_marker = True
|
2010-06-18 12:31:10 +04:00
|
|
|
lines.append(line)
|
2012-11-12 14:20:05 +04:00
|
|
|
|
2010-06-18 12:31:10 +04:00
|
|
|
# line not ending in \r\n or without ':' isn't a
|
|
|
|
# valid header and starts multiline response
|
|
|
|
if not line.endswith('\r\n') or ':' not in line:
|
|
|
|
multiline = True
|
2010-06-18 23:27:20 +04:00
|
|
|
# Response: Follows indicates we should wait for end
|
|
|
|
# marker --END COMMAND--
|
2011-06-01 04:12:23 +04:00
|
|
|
if not (multiline or status) and line.startswith('Response') and \
|
2012-11-11 21:43:41 +04:00
|
|
|
line.split(':', 1)[1].strip() == 'Follows':
|
2010-06-18 23:27:20 +04:00
|
|
|
wait_for_marker = True
|
2010-06-18 12:31:10 +04:00
|
|
|
# same when seeing end of multiline response
|
|
|
|
if multiline and line.startswith('--END COMMAND--'):
|
2010-06-18 23:27:20 +04:00
|
|
|
wait_for_marker = False
|
2010-06-18 12:31:10 +04:00
|
|
|
multiline = False
|
2011-06-01 04:12:23 +04:00
|
|
|
# same when seeing end of status response
|
|
|
|
if status and 'StatusComplete' in line:
|
|
|
|
wait_for_marker = False
|
|
|
|
status = False
|
2010-06-18 12:31:10 +04:00
|
|
|
if not self._connected.isSet():
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
# EOF during reading
|
2010-06-17 20:16:28 +04:00
|
|
|
self._sock.close()
|
|
|
|
self._connected.clear()
|
|
|
|
# if we have a message append it to our queue
|
|
|
|
if lines and self._connected.isSet():
|
2010-06-18 12:31:10 +04:00
|
|
|
self._message_queue.put(lines)
|
2010-06-17 20:16:28 +04:00
|
|
|
else:
|
|
|
|
self._message_queue.put(None)
|
2010-06-18 12:31:10 +04:00
|
|
|
except socket.error:
|
|
|
|
self._sock.close()
|
|
|
|
self._connected.clear()
|
|
|
|
self._message_queue.put(None)
|
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def register_event(self, event, function):
|
|
|
|
"""
|
|
|
|
Register a callback for the specfied event.
|
|
|
|
If a callback function returns True, no more callbacks for that
|
|
|
|
event will be executed.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# get the current value, or an empty list
|
|
|
|
# then add our new callback
|
|
|
|
current_callbacks = self._event_callbacks.get(event, [])
|
|
|
|
current_callbacks.append(function)
|
|
|
|
self._event_callbacks[event] = current_callbacks
|
|
|
|
|
|
|
|
def unregister_event(self, event, function):
|
|
|
|
"""
|
|
|
|
Unregister a callback for the specified event.
|
|
|
|
"""
|
|
|
|
current_callbacks = self._event_callbacks.get(event, [])
|
|
|
|
current_callbacks.remove(function)
|
|
|
|
self._event_callbacks[event] = current_callbacks
|
|
|
|
|
|
|
|
def message_loop(self):
|
|
|
|
"""
|
|
|
|
The method for the event thread.
|
|
|
|
This actually recieves all types of messages and places them
|
|
|
|
in the proper queues.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# start a thread to recieve data
|
|
|
|
t = threading.Thread(target=self._receive_data)
|
|
|
|
t.setDaemon(True)
|
|
|
|
t.start()
|
|
|
|
|
|
|
|
try:
|
|
|
|
# loop getting messages from the queue
|
|
|
|
while self._running.isSet():
|
|
|
|
# get/wait for messages
|
|
|
|
data = self._message_queue.get()
|
|
|
|
|
|
|
|
# if we got None as our message we are done
|
|
|
|
if not data:
|
|
|
|
# notify the other queues
|
|
|
|
self._event_queue.put(None)
|
|
|
|
for waiter in self._reswaiting:
|
|
|
|
self._response_queue.put(None)
|
|
|
|
break
|
|
|
|
|
|
|
|
# parse the data
|
|
|
|
message = ManagerMsg(data)
|
|
|
|
|
|
|
|
# check if this is an event message
|
|
|
|
if message.has_header('Event'):
|
|
|
|
self._event_queue.put(Event(message))
|
|
|
|
# check if this is a response
|
|
|
|
elif message.has_header('Response'):
|
|
|
|
self._response_queue.put(message)
|
|
|
|
else:
|
2014-10-07 22:49:54 +04:00
|
|
|
print('No clue what we got\n%s' % message.data)
|
2010-06-17 20:16:28 +04:00
|
|
|
finally:
|
|
|
|
# wait for our data receiving thread to exit
|
|
|
|
t.join()
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def event_dispatch(self):
|
2010-06-18 12:31:10 +04:00
|
|
|
"""This thread is responsible for dispatching events"""
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
# loop dispatching events
|
|
|
|
while self._running.isSet():
|
|
|
|
# get/wait for an event
|
|
|
|
ev = self._event_queue.get()
|
|
|
|
|
|
|
|
# if we got None as an event, we are finished
|
|
|
|
if not ev:
|
|
|
|
break
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# dispatch our events
|
|
|
|
|
|
|
|
# first build a list of the functions to execute
|
2010-06-18 19:24:05 +04:00
|
|
|
callbacks = (self._event_callbacks.get(ev.name, [])
|
2012-11-11 21:43:41 +04:00
|
|
|
+ self._event_callbacks.get('*', []))
|
2010-06-17 20:16:28 +04:00
|
|
|
|
2011-05-31 23:56:57 +04:00
|
|
|
# now execute the functions
|
2010-06-17 20:16:28 +04:00
|
|
|
for callback in callbacks:
|
2012-11-11 21:43:41 +04:00
|
|
|
if callback(ev, self):
|
|
|
|
break
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
def connect(self, host, port=5038):
|
|
|
|
"""Connect to the manager interface"""
|
|
|
|
|
|
|
|
if self._connected.isSet():
|
|
|
|
raise ManagerException('Already connected to manager')
|
|
|
|
|
|
|
|
# make sure host is a string
|
2014-10-07 22:49:54 +04:00
|
|
|
assert type(host) is str
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
port = int(port) # make sure port is an int
|
|
|
|
|
|
|
|
# create our socket and connect
|
|
|
|
try:
|
2010-06-18 12:31:10 +04:00
|
|
|
_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
2012-11-11 21:43:41 +04:00
|
|
|
_sock.connect((host, port))
|
2015-02-26 22:10:32 +03:00
|
|
|
if PY3:
|
|
|
|
self._sock = _sock.makefile(mode='rwb', buffering=0)
|
|
|
|
else:
|
|
|
|
self._sock = _sock.makefile()
|
2012-11-11 21:43:41 +04:00
|
|
|
_sock.close()
|
2014-10-07 22:49:54 +04:00
|
|
|
except socket.error as e:
|
|
|
|
raise ManagerSocketException(e.errno, e.strerror)
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
# we are connected and running
|
|
|
|
self._connected.set()
|
|
|
|
self._running.set()
|
|
|
|
|
|
|
|
# start the event thread
|
|
|
|
self.message_thread.start()
|
|
|
|
|
|
|
|
# start the event dispatching thread
|
|
|
|
self.event_dispatch_thread.start()
|
|
|
|
|
|
|
|
# get our initial connection response
|
|
|
|
return self._response_queue.get()
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
"""Shutdown the connection to the manager"""
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
# if we are still running, logout
|
|
|
|
if self._running.isSet() and self._connected.isSet():
|
|
|
|
self.logoff()
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
if self._running.isSet():
|
|
|
|
# put None in the message_queue to kill our threads
|
|
|
|
self._message_queue.put(None)
|
|
|
|
|
|
|
|
# wait for the event thread to exit
|
|
|
|
self.message_thread.join()
|
|
|
|
|
|
|
|
# make sure we do not join our self (when close is called from event handlers)
|
|
|
|
if threading.currentThread() != self.event_dispatch_thread:
|
|
|
|
# wait for the dispatch thread to exit
|
|
|
|
self.event_dispatch_thread.join()
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
self._running.clear()
|
|
|
|
|
2010-06-18 17:47:45 +04:00
|
|
|
# Manager actions
|
2010-06-17 20:16:28 +04:00
|
|
|
|
|
|
|
def login(self, username, secret):
|
|
|
|
"""Login to the manager, throws ManagerAuthException when login falis"""
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Login'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Username'] = username
|
|
|
|
cdict['Secret'] = secret
|
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
if response.get_header('Response') == 'Error':
|
2012-11-11 21:43:41 +04:00
|
|
|
raise ManagerAuthException(response.get_header('Message'))
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def ping(self):
|
|
|
|
"""Send a ping action to the manager"""
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Ping'}
|
2010-06-17 20:16:28 +04:00
|
|
|
response = self.send_action(cdict)
|
|
|
|
return response
|
|
|
|
|
|
|
|
def logoff(self):
|
|
|
|
"""Logoff from the manager"""
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Logoff'}
|
2010-06-17 20:16:28 +04:00
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def hangup(self, channel):
|
2010-06-17 20:22:48 +04:00
|
|
|
"""Hangup the specified channel"""
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Hangup'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Channel'] = channel
|
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
def status(self, channel=''):
|
2010-06-17 20:16:28 +04:00
|
|
|
"""Get a status message from asterisk"""
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Status'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Channel'] = channel
|
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def redirect(self, channel, exten, priority='1', extra_channel='', context=''):
|
|
|
|
"""Redirect a channel"""
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Redirect'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Channel'] = channel
|
|
|
|
cdict['Exten'] = exten
|
|
|
|
cdict['Priority'] = priority
|
2012-11-11 21:43:41 +04:00
|
|
|
if context:
|
|
|
|
cdict['Context'] = context
|
|
|
|
if extra_channel:
|
|
|
|
cdict['ExtraChannel'] = extra_channel
|
2010-06-17 20:16:28 +04:00
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def originate(self, channel, exten, context='', priority='', timeout='', caller_id='', async=False, account='', variables={}):
|
|
|
|
"""Originate a call"""
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Originate'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Channel'] = channel
|
|
|
|
cdict['Exten'] = exten
|
2012-11-11 21:43:41 +04:00
|
|
|
if context:
|
|
|
|
cdict['Context'] = context
|
|
|
|
if priority:
|
|
|
|
cdict['Priority'] = priority
|
|
|
|
if timeout:
|
|
|
|
cdict['Timeout'] = timeout
|
|
|
|
if caller_id:
|
|
|
|
cdict['CallerID'] = caller_id
|
|
|
|
if async:
|
|
|
|
cdict['Async'] = 'yes'
|
|
|
|
if account:
|
|
|
|
cdict['Account'] = account
|
2010-06-17 20:16:28 +04:00
|
|
|
# join dict of vairables together in a string in the form of 'key=val|key=val'
|
|
|
|
# with the latest CVS HEAD this is no longer necessary
|
|
|
|
# if variables: cdict['Variable'] = '|'.join(['='.join((str(key), str(value))) for key, value in variables.items()])
|
2012-11-11 21:43:41 +04:00
|
|
|
if variables:
|
|
|
|
cdict['Variable'] = ['='.join(
|
|
|
|
(str(key), str(value))) for key, value in variables.items()]
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def mailbox_status(self, mailbox):
|
|
|
|
"""Get the status of the specfied mailbox"""
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'MailboxStatus'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Mailbox'] = mailbox
|
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def command(self, command):
|
|
|
|
"""Execute a command"""
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Command'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Command'] = command
|
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
|
|
|
def extension_state(self, exten, context):
|
|
|
|
"""Get the state of an extension"""
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'ExtensionState'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Exten'] = exten
|
|
|
|
cdict['Context'] = context
|
|
|
|
response = self.send_action(cdict)
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
return response
|
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
def playdtmf(self, channel, digit):
|
2010-06-17 20:22:48 +04:00
|
|
|
"""Plays a dtmf digit on the specified channel"""
|
2014-09-18 00:39:01 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'PlayDTMF'}
|
2010-06-17 20:22:48 +04:00
|
|
|
cdict['Channel'] = channel
|
|
|
|
cdict['Digit'] = digit
|
|
|
|
response = self.send_action(cdict)
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
def absolute_timeout(self, channel, timeout):
|
|
|
|
"""Set an absolute timeout on a channel"""
|
2011-05-31 23:56:57 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'AbsoluteTimeout'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Channel'] = channel
|
|
|
|
cdict['Timeout'] = timeout
|
|
|
|
response = self.send_action(cdict)
|
|
|
|
return response
|
|
|
|
|
|
|
|
def mailbox_count(self, mailbox):
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'MailboxCount'}
|
2010-06-17 20:16:28 +04:00
|
|
|
cdict['Mailbox'] = mailbox
|
|
|
|
response = self.send_action(cdict)
|
|
|
|
return response
|
|
|
|
|
2010-06-18 17:47:45 +04:00
|
|
|
def sippeers(self):
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'Sippeers'}
|
2010-06-18 17:47:45 +04:00
|
|
|
response = self.send_action(cdict)
|
|
|
|
return response
|
|
|
|
|
|
|
|
def sipshowpeer(self, peer):
|
2012-11-11 21:43:41 +04:00
|
|
|
cdict = {'Action': 'SIPshowpeer'}
|
2010-06-18 17:47:45 +04:00
|
|
|
cdict['Peer'] = peer
|
|
|
|
response = self.send_action(cdict)
|
|
|
|
return response
|
|
|
|
|
2014-09-18 00:39:01 +04:00
|
|
|
def reload(self, module):
|
|
|
|
""" Reloads config for a given module """
|
|
|
|
|
|
|
|
cdict = {'Action': 'Reload'}
|
|
|
|
cdict['Module'] = module
|
|
|
|
response = self.send_action(cdict)
|
|
|
|
return response
|
|
|
|
|
2010-06-18 17:47:45 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
class ManagerException(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class ManagerSocketException(ManagerException):
|
|
|
|
pass
|
|
|
|
|
2010-06-17 20:16:28 +04:00
|
|
|
|
2012-11-11 21:43:41 +04:00
|
|
|
class ManagerAuthException(ManagerException):
|
|
|
|
pass
|