At the beginning of time, the only "supplier" of Asterisk functionality was modules, many of which expanded the arsenal of applications and features of the dial plan.
Then, at the beginning of time, all of these commands and functions were far ahead of their time, and thanks to them Asterisk “worked” many commercial products on functionality.
If there was any need to go beyond the limits of existing applications and functions, you could write your own module in C, and this was the only way to extend the functionality and exit from the existing “cell”, no matter how spacious it might be.
But the development of the Asterisk module in the C language is hardly a trivial task. This is a very thorny path, and it is also very risky, because a critical error in its module easily led to the complete fall of Asterisk in the core.
More “soft” and simpler ways were needed to expand functions and integrate with other systems.
So the AGI and AMI interfaces appeared.
Asterisk Gateway Interface (AGI) is a synchronous dialplan interface, architecturally "lapped" with CGI. The AGI dialplan team started the process, and used standard input and output to receive commands and transfer results. With the help of AGI, you can solve problems of integration with external systems, for example, you can go to the corporate database and find the name of the calling customer by his number.
In essence, AGI provided a way to write Asterisk's recruitment plan not in the extensions.conf format, but in its own programming language, using the commands and functions supplied by the modules, around which its own business logic is built.
Asterisk Manager Interface (AMI) is an asynchronous (event-based) interface that allows you to monitor the internal state of objects in Asterisk and receive information about events that occur. If AGI architecturally resembles a CGI interface, then the AMI session is similar to a telnet session, in which a third-party application connects via TCP / IP to the AMI port of Asterisk, and can send its commands, the response to which comes after some time in the form of a response event. In addition to responding to commands in the AMI connection, various events occurring in Asterisk are “falling”, and it is up to the client to determine whether they belong to him or they can be simply ignored.
About AGI, we can say that this is a call execution mechanism, and about AMI - that this is a call control mechanism. Most often, to build your telecommunications application, you must use AGI and AMI together right away. There is a "smearing" of business logic across different applications, which complicates its understanding and further maintenance and development.
In addition, there are several limitations:
As a result, in order to break out of the framework of the existing restrictions on commands and functions, it is necessary to write your C-module that implements a low-level telephone primitive, and integrate with external systems using AGI & AMI.
That was before the appearance of Asterisk REST Interface .
Basic concepts of ARI:
"Three whales" ARI:
An example of a dialplan passing control to Stais:
exten => _X.,1,Stasis(myapp,arg1,arg2) exten => _X.,n,NoOp(Left Stasis)
ARI has some limitations
Consider the categories of operations available in ARI:
And we will focus on each category in more detail.
For a complete list of possible operations, see the asterisk wiki - https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+ARI
I will give a partial list of events that are available on the web socket of the connected application:
Well, in conclusion, I will give an example of originating a call using the Python ARI library.
In this example, it originates at the specified peer, and a cause code is returned:
#!/usr/bin/env python2.7 # Requirements: pip install ari gevent import argparse import ari import gevent from gevent.monkey import patch_all; patch_all() from gevent.event import Event import logging from requests.exceptions import HTTPError, ConnectionError import socket import time logging.basicConfig() # Important! # Otherwise you get No handlers could be found for # logger "ari.client" ARI_URL = 'http://192.168.56.101:8088/ari' ARI_USER = 'test' ARI_PASSWORD = 'test' client = ari.connect(ARI_URL, ARI_USER, ARI_PASSWORD) def run(): try: client.run('originator') except socket.error as e: if e.errno == 32: # Broken pipe as we close the client. pass except ValueError as e: if e.message == 'No JSON object could be decoded': # client.close() pass def originate(endpoint=None, callerid=None, context=None, extension=None, priority=None, timeout=None): # Go! evt = Event() # Wait flag for origination result = {} gevent.sleep(0.1) # Hack to let run() arrange all. start_time = time.time() try: channel = client.channels.originate( endpoint=endpoint, callerId=callerid, app='originator', timeout=timeout ) def state_change(channel, event): state = event['channel']['state'] if state == 'Up': channel = channel.continueInDialplan( context=context, extension=extension, priority=priority) def destroyed(channel, event): end_time = time.time() result['status'] = 'success' result['message'] = '%s (%s)' % ( event.get('cause_txt'), event.get('cause')) result['duration'] = '%0.2f' % (end_time - start_time) evt.set() channel.on_event('ChannelDestroyed', destroyed) channel.on_event('ChannelStateChange', state_change) # Wait until we get origination result evt.wait() client.close() return except HTTPError as e: result['status'] = 'error' try: error = e.response.json().get('error') result['message'] = e.response.json().get('error') except Exception: result['message'] = e.response.content finally: print result client.close() def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('endpoint', type=str, help='Endpoint, eg SIP/operator/123456789') parser.add_argument('callerid', type=str, help='CallerID, eg 111111') parser.add_argument('context', type=str, help='Asterisk context to connect call, eg default') parser.add_argument('extension', type=str, help='Context\'s extension, eg s') parser.add_argument('priority', type=str, help='Context\'s priority, eg 1') parser.add_argument('timeout', type=int, help='Originate timeout, eg 60') return parser.parse_args() if __name__ == '__main__': args = parse_args() runner = gevent.spawn(run) originator = gevent.spawn(originate, endpoint=args.endpoint, callerid=args.callerid, context=args.context, extension=args.extension, priority=args.priority, timeout=args.timeout ) gevent.joinall([originator, runner])
This script can be run from the console, and here is what it returns:
(env)MacBook-Pro-Max:barrier max$ ./ari_originate.py SIP/operator 11111 default s 1 4 {'status': 'success', 'duration': '2.54', 'message': u'Normal Clearing (16)'}
Parameter designations:
(env)MacBook-Pro-Max:barrier max$ ./ari_originate.py -h usage: ari_originate.py [-h] endpoint callerid context extension priority timeout positional arguments: endpoint Endpoint, eg SIP/operator/123456789 callerid CallerID, eg 111111 context Asterisk context to connect call, eg default extension Context's extension, eg s priority Context's priority, eg 1 timeout Originate timeout, eg 60 optional arguments: -h, --help show this help message and exit
To run this script, you need to install the ari and gevent libraries:
pip install ari gevent
PS Written based on the author's speech at Asterconf 2016.
PPS The script is here - https://gist.github.com/litnimax/2b0f9d99e49e49a07e59c45496112133
Source: https://habr.com/ru/post/308652/