John Malkovich: I have seen a world that NO man should see!
Craig Schwartz: Really? Because for most people it’s a rather enjoyable experience.
Being John Malkovich
The Asterisk Manager Interface (AMI) is a system monitoring and management interface provided by Asterisk. It allows live monitoring of events that occur in the system, as well as enabling requests for Asterisk to perform some action. The available actions are wide-ranging and include things such as returning status information or originating new calls. Many interesting applications have been developed on top of Asterisk that use the AMI as their primary interface to Asterisk.
This chapter also includes documentation on the use of call files. Asterisk’s call files are an easy way to originate a few calls. Once call origination volume increases or your needs become otherwise more complex, you can move on to using the AMI. In fact, we find call files to be useful enough that we’re going to talk about them first.
It is common to use AMI for originating calls, but in many situations it’s easier to use call files. A call file is a simple text file that describes the call that you would like Asterisk to originate. When a call file is placed into the /var/spool/asterisk/outgoing directory, Asterisk will immediately detect that a file has been placed there and process the call.
Asterisk comes with a sample call file, which you will find at ~/src/asterisk-15.<TAB>/sample.call (or wherever the root directory of your Asterisk source is located).
For your first call file, let’s create a call between two of your
telephones. Make sure you have at least two of your phones registered
and working. For this example we’ll be using SOFTPHONE_A
and SOFTPHONE_B.
Create the following file in your home directory:
$ vim ~/call-file Channel: PJSIP/SOFTPHONE_A Extension: 103 Context: sets
Make a copy of this file (so that you don’t have to re-create it every time you want to run it):
$ cp ~/call-file docall
Change the ownership of the docall file to asterisk
:
$ chown asterisk:asterisk docall
Move the docall file into Asterisk’s outgoing folder.
$ sudo mv docall /var/spool/asterisk/outgoing
Sometimes, the easiest way is the best way.
The use of mv
instead of
cp
here is important. Asterisk is watching for
contents to show up in the spool directory. If
you use copy, Asterisk may try to read the new file before the
contents have been copied into it. Creating a file and then moving
it avoids this problem.
Get comfortable with using call files, and you may find them solving problems you’d otherwise have to perform far more work to achieve.
The Channel
component of the
call file is required. Normally, a call coming into Asterisk is
initiated by the endpoint (for example, you make a call from your
phone). In a call file, that connection has to happen the other way
around—Asterisk reaches out to the endpoint, and only when it answers
can the call start. Plan accordingly.
You also must specify the Context
where the call will begin once the
initial channel has answered. This can be useful, since it means you can
connect the call through a context that wouldn’t normally be accessible
to that channel, but in practice we’d suggest you simply provide the
same context that channel would have entered the dialplan through if it had initiated the
call normally.
The Extension
must of course
also be specified. This would typically be the phone number that is to
be called, but of course it could be any valid extension within the
Context
.
The rest of the parameters of the call file are optional, and are detailed both in the ~/src/asterisk-15.<TAB>/sample.call file, and on the Asterisk wiki website.
This section is for getting your hands dirty with the AMI as quickly as possible. First, put the following configuration in /etc/asterisk/manager.conf:
; Turn on the AMI and ask it to only accept connections from localhost. [general] enabled = yes webenabled = yes bindaddr = 127.0.0.1 ; Create an account called "hello", with a password of "world" [hello] secret=world read=all ; Receive all types of events write=all ; Allow this user to execute all actions
This sample configuration is set up to allow only local connections to the AMI. If you intend to make this interface available over a network, it is strongly recommended that you only do so using TLS. The use of TLS is discussed in more detail later in this chapter.
Once the AMI configuration is ready, enable the built-in HTTP server by putting the following contents in /etc/asterisk/http.conf:
; Enable the built-in HTTP server, and only listen for connections on localhost. [general] enabled = yes bindaddr = 127.0.0.1
Reload the manager and http servers from the Asterisk CLI:
*CLI> manager reload *CLI> module reload http
There are multiple ways to connect to the AMI, but a TCP socket is the most
common. We will use telnet
to demonstrate AMI
connectivity. We’ll need to install telnet
for
this:
$ sudo yum -y install telnet
This example shows these steps:
Connect to the AMI over a TCP socket on port 5038.
Log in using the Login
action.
Execute the Ping
action.
Log off using the Logoff
action.
Here’s how to do that using
telnet
:
$ telnet localhost 5038 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Asterisk Call Manager/4.0.3
You’ve connected, but it’s going to hang up on you unless you
authenticate yourself. Paste the following into the
telnet
window:
Action: Login Username: hello Secret: world
Note there needs to be a blank line after the commands (press Enter after you paste everything if nothing happens).
Response: Success Message: Authentication accepted
OK, it likes us. Let’s run a simple command just to verify it’s really talking to us:
Action: Ping
Response: Success Ping: Pong
So far, so good. We’ll just tidy up and log off now.
Action: Logoff
Response: Goodbye Message: Thanks for all the fish. Connection closed by foreign host.
You have verified that AMI is accepting connections via a TCP connection.
It is also possible to use the AMI over HTTP. We will perform the same actions as before, but over HTTP instead of the native TCP interface to the AMI. AMI over HTTP is covered in more detail in “AMI over HTTP”.
Accounts used for connecting to the AMI over HTTP are the same accounts configured in /etc/asterisk/manager.conf.
This example demonstrates how to access the
AMI over HTTP, log in, execute the Ping
action, and log off:
$ curl "http://localhost:8088/rawman?action=login&username=hello&secret=world" -c /tmp/tempcookie Response: Success Message: Authentication accepted
$ curl "http://localhost:8088/rawman?action=ping" -b /tmp/tempcookie Response: Success Ping: Pong Timestamp: 1538871944.474131
$ curl "http://localhost:8088/rawman?action=logoff" -b /tmp/tempcookie Response: Goodbye Message: Thanks for all the fish.
The HTTP interface to AMI lets you integrate Asterisk call control into a web service.
The section “AMI Quick Start” showed a very basic set of configuration files to get you started. There are many ways you can fine-tune the configuration of the AMI.
The main configuration file for the AMI is /etc/asterisk/manager.conf. The [general]
section contains options that
control the overall operation of the AMI. Any other sections in the
manager.conf file will define
accounts for logging in and using the AMI. The sample file contains
detailed explanations of the various parameters, and can be found in
~/src/asterisk-15<TAB>/configs/samples/manager.conf.sample.
If you are going to expose your AMI outside the machine it is running on, you will want to configure TLS connectivity.
The manager.conf configuration file also contains
the configuration of AMI user accounts. You create an account by adding
a section with the username inside square brackets. Within each [
section
there are options that can be set that will apply only to that account.
The ~/src/asterisk-15<TAB>/configs/samples/manager.conf.sample
file also contains detailed explanations of each of these parameters.
Our user named username
][hello]
, has the
simplest configuration, which is to allow all read and write actions.
You should normally create AMI users that are restricted to only the
actions necessary to their functioning.
Within the [
section,
the username
]read
and write
options set which manager actions and
manager events a particular user has access to. At this writing there
are 20 of them: all
, system
, call
, log
,
verbose
, agent
, user
, config
, command
, dtmf
, reporting
, cdr
, dialplan
, originate
, agi
, cc
,
aoc
, test
, security
, and message
. You will find the
manager.conf.sample file contains a reference for
each of these that is relevant to your release (and, if any are added
that have not been listed here, they will be in the sample file).
Take special notice of the system
, command
, and originate
permissions. These permissions
grant significant power to any applications that are authorized to use
them. Only grant these permissions to applications that you have full
control over (and ideally are running on the same box).
As we’ve seen, the Asterisk Manager Interface
can be accessed over HTTP as well as TCP. To make that work, a very
simple HTTP server is embedded in Asterisk. All of the options relevant
to the AMI go in the [general]
section of /etc/asterisk/http.conf.
Enabling access to the AMI over HTTP requires
both /etc/asterisk/manager.conf
and /etc/asterisk/http.conf. The
AMI must be enabled in manager.conf with the enabled
option set to yes
, and the manager.conf option webenabled
must be set to yes
to allow access over HTTP. Finally, the
enabled
option in http.conf must be set to yes
to turn on the HTTP server
itself.
The available options will be found in your ~/src/asterisk-15<TAB>/configs/samples/http.conf.sample file.
There are two main types of messages on the Asterisk Manager Interface: manager events and manager actions.
Manager events are one-way messages sent from Asterisk to AMI clients to report something that has occurred on the system (Figure 17-1).
Manager actions are requests
from a client to Asterisk to perform some action and return the result
(Figure 17-2). For example, the AMI action Originate
requests that Asterisk create a new
call, and naturally the client application will need responses from
Asterisk to indicate the progress of that activity.
Other manager actions are requests for data. For example, there is a manager action to get a list of all active channels on the system: the details about each channel are delivered as a manager event. When the list of results is complete, a final message will be sent to indicate that the end has been reached. See Figure 17-3 for a graphical representation of a client sending this type of manager action and receiving a list of responses.
All AMI messages, including manager events, manager actions, and manager action responses, are encoded the same way. The messages are text-based, with lines terminated by a carriage return and a line-feed character. A message is terminated by a blank line:
Header1: This is the first header<CR><LF> Header2: This is the second header<CR><LF> Header3: This is the last header of this message<CR><LF> <CR><LF>
If you are running tests from a telnet client, what this means is that after the last line of instructions, you’ll need to press the Enter key twice.
Manager events always have an Event
header and a Privilege
header. The Event
header gives the name of the event,
while the Privilege
header lists
the permission levels associated with the event. Any other headers
included with the event are specific to the event type. Here’s an
example:
Event: Hangup
Privilege: call,all
Channel: SIP/0004F2060EB4-00000000
Uniqueid: 1283174108.0
CallerIDNum: 2565551212
CallerIDName: Russell Bryant
Cause: 16
Cause-txt: Normal Clearing
The Asterisk CLI includes the commands manager show
events
and manager show event
. Run these commands
at the Asterisk CLI to get a list of events or to find out the details
of a specific event.<event>
Don’t forget that an excellent reference for all things Asterisk, including the AMI, is the official Asterisk wiki.
When executing a manager action, you
must include the Action
header. The Action
header identifies which manager
action is being executed. The rest of the headers are arguments to the
manager action, and may or may not be required depending on the
action.
To get a list of the headers associated with a
particular manager action, type manager show command
at the
Asterisk CLI. To get a full
list of manager actions supported by the version of Asterisk you are
running, enter <Action>
manager show commands
at the
Asterisk CLI.
The final response to a manager action is
typically a message that includes the Response
header. The value of the Response
header will be Success
if the manager action was
successfully executed. If the manager action was not successfully
executed, the value of the Response
header will be Error
. For
example:
Action: Login
Username: hello
Secret: world
Response: Success
Message: Authentication accepted
In addition to the native TCP interface, it is also possible to access the Asterisk Manager Interface over HTTP. Programmers with previous experience writing applications that use web APIs will likely prefer this over the native TCP connectivity. While the TCP interface only offers a single type of message structure, AMI over HTTP offers a few encoding options. You can receive responses in the same format as the TCP interface, in XML, or as a basic HTML page. The encoding type is chosen based on a field in the request URL. The encoding options are discussed in more detail later in this section.
There are two methods of performing authentication against the AMI over HTTP. The first is
to use the Login
action, similar to
authentication with the native TCP interface. This is the method that
was used in the quick-start example, as seen in “AMI over HTTP”.
Once successfully authenticated, Asterisk will provide a cookie that
identifies the authenticated session. Here is an example response to
the Login
action that includes a
session cookie from Asterisk:
$ curl -v "http://localhost:8088/rawman?action=login&username=hello&secret=world"
The second authentication option is HTTP digest authentication.
In this example, the requested encoding type based on the request URL
is rawman
. To indicate that HTTP
digest authentication should be used, prefix the encoding type in the
request URL with an a
:
$ curl -v --digest -u hello:world http://127.0.0.1:8088/arawman?action=ping
The rawman
encoding type is what has been used in all the AMI over HTTP
examples in this chapter so far. The responses received from requests
using rawman
are formatted in the
exact same way that they would be if the requests were sent over a
direct TCP connection to the AMI.
curl -v "http://localhost:8088/rawman?action=login&username=hello&secret=world" curl -v --digest -u hello:world http://127.0.0.1:8088/arawman?action=ping
The manager
encoding type provides a response in simple HTML form. This
interface is primarily useful for experimenting with the AMI:
$ curl -v "http://localhost:8088/manager?action=login&username=hello&secret=world" $ curl -v --digest -u hello:world http://localhost:8088/amanager?action=ping
When connected to the native TCP interface for
the AMI, manager events are delivered asynchronously. When using the
AMI over HTTP, you must retrieve events by polling for them. You
retrieve events over HTTP by executing the WaitEvent
manager action. The following
example shows how events can be retrieved using the WaitEvent
manager action. The steps
are:
Start an HTTP AMI session using the
Login
action.
Register a SIP phone to Asterisk to generate a manager event.
Retrieve the manager event using the
WaitEvent
action.
The interaction looks like this:
$wget --save-cookies cookies.txt
>"http://localhost:8088/mxml?action=login&username=hello&secret=world" -O -
<ajax-response> <response type='object' id='unknown'> <generic response='Success' message='Authentication accepted' /> </response> </ajax-response> $wget --load-cookies cookies.txt
<"http://localhost:8088/mxml?action=waitevent" -O -
<ajax-response> <response type='object' id='unknown'> <generic response='Success' message='Waiting for Event completed.' /> </response> <response type='object' id='unknown'> <generic event='PeerStatus' privilege='system,all' channeltype='SIP' peer='SIP/0000FFFF0004' peerstatus='Registered' address='172.16.0.160:5060' /> </response> <response type='object' id='unknown'> <generic event='WaitEventComplete' /> </response> </ajax-response>
You’ll need to develop mechanisms in your application to ensure that buffered events are frequently polled.
Most of this chapter so far discussed the concepts and configuration related to the AMI. This section will provide some example usage.
The AMI has the Originate
manager action that can be used to originate a call. Many of the
accepted headers are the same as the options placed in call files. Table 17-1 lists the headers accepted by the
Originate
action.
Option | Example value | Description |
---|---|---|
ActionID | a3a58876-f7c9-4c28-aa97-50d8166f658d | This header is accepted by most AMI actions. It is used to provide a unique identifier that will also be included in all responses to the action. It gives you a way to identify which request a response is associated with. This is important since all actions, their responses, and events are all transmitted over the same connection (unless using AMI over HTTP). |
Channel | SIP/myphone | This header is critical and must be specified. This
describes the outbound call that will be originated. The value
is the same syntax that would be used for the channel argument
to the Dial() application in
the dialplan. |
Context | default | This header is used to specify a location in the dialplan
to start executing once the outbound call has answered. The
Context , Exten , and Priority headers must be used
together. When using these headers, the Application and Data headers should not be
used. |
Exten | s | See the documentation for the Context header. |
Priority | 1 | See the documentation for the Context header. |
Application | ConfBridge | The Application and
Data headers can be used
instead of the Context ,
Exten , and Priority headers. In this case, the
outbound call is directly connected to a single application once
the call has been answered. |
Data | 500 | See the documentation for the Application header. |
Timeout | 30000 | This header specifies how long to wait in milliseconds
for an answer before giving up on an outbound call. The default
is 30000 milliseconds (30
seconds). |
CallerID | Matthew Jordan <(555) 867-5309> | This header can be used to specify the caller ID used for the outbound call. |
Account | someaccount | This header sets the CDR account code for the outbound call. |
Variable | VARIABLE=VALUE or
FUNCTION(arguments)=VALUE | The Variable header
can be used to set both channel variables or channel functions
on the outbound channel. It can be specified multiple
times. |
Codecs | ulaw,alaw | This option can be used to limit which codecs are allowed for the outbound call. If not specified, the set of codecs configured in the channel driver configuration file will still be honored. |
EarlyMedia | true | If this header is specified and set to true , the outbound call will get
connected to the specified extension or application as soon as
there is any early media. |
Async | true | If this header is specified and set to true , this call will be originated
asynchronously. This will allow you to continue executing other
actions on the AMI connection while the call is being
processed. |
The simplest example of using the Originate
action is via
telnet
:
$ telnet localhost 5038 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Asterisk Call Manager/4.0.3
Once the connection is established, you need to log in.
Action: Login Username: hello Secret: world
Response: Success Message: Authentication accepted
Now you’re ready to originate your call. We’re doing essentially the same thing we did with the call file, only this time using the AMI:
Action: Originate Channel: PJSIP/SOFTPHONE_A Context: sets Exten: 103 Priority: 1
You should hear SOFTPHONE_A
ringing. As soon as you answer it, a call will be placed to
SOFTPHONE_B
.
AMI is no longer involved in what’s going on. You can disconnect and the call will continue (leave the call up for now, as we’re going to work with the in-progress call next).
Action: Logoff
Response: Goodbye Message: Thanks for all the fish. Connection closed by foreign host.
If you’ve already hung up the call, that’s no problem. You’ll just need to re-establish the call, which of course you can do simply by calling one extension from the other (101 to 103, or whatever you want).
Redirecting (or transferring) a call from the AMI is another feature worth mentioning.
The Redirect
AMI action can be used
to send one or two channels to any other extension in the Asterisk
dialplan. If you need to redirect two channels that are bridged
together, do them both at the same time. Otherwise, once one channel has
been redirected, the other will be hung up.
You can redirect a single channel (the other channel will be disconnected):
Action: Redirect Channel: PJSIP/SOFTPHONE_A-00000013 Exten: 209 Context: sets Priority: 1
Or you can redirect two channels:
Action: Redirect Channel: PJSIP/SOFTPHONE_A-00000015 Context: sets Exten: 209 Priority: 1 ExtraChannel: PJSIP/SOFTPHONE_B-00000016 ExtraContext: sets ExtraExten: 209 ExtraPriority: 1
The redirect function allows you to create powerful external applications that can control calls in progress.
Many application developers write code that directly interfaces with the AMI. However, there are a number of frameworks that have been created with the purpose of making AMI application development easier. If you search for Asterisk frameworks in the popular programming language of your choice, you are likely to find one. The onus is on you to determine the suitability of the framework you are interested in. Some things you should look for in a framework include:
Has this project been around for a few years? A mature project is far less likely to have serious bugs in it.
Check the age of the latest update. If the project hasn’t been updated in five years, there’s a strong possibility it has been abandoned. It might still be usable, but you’ll be on your own. Similarly, what does the bug tracker look like? Are there a lot of important bugs being ignored? (Be discerning here, since often the realities of maintaining a free project require disciplined triage—not everybody’s features are going to get added.)
Is this a well-written framework? If it was not engineered well, you should be aware of that when deciding whether to trust your project to it.
Is there an active community of developers using this project? It’s likely you’ll need help; will it be available when you need it?
The code should be well commented, but ideally, a wiki or other official documentation to support the library is essential.
Table 17-2 lists some frameworks we have found that, as of this writing, met the preceding criteria. There may be others out there.
Framework | Language |
---|---|
Adhearsion | Ruby |
StarPy | Python |
Asterisk-Java | Java |
AsterNET | .NET |
ami-io | Node.js |
panoramisk | Python |
The Asterisk Manager Interface provides an API for monitoring events from an Asterisk system, as well as requesting that Asterisk perform a wide range of actions. An HTTP interface has been provided, and a number of frameworks have been developed, that make it easier to develop applications.