Push it Real Good! (or ARI Push Configuration)

Push it Real Good! (or ARI Push Configuration)

Veterans of Asterisk configuration have likely dealt with static configuration files or realtime before. Since Asterisk 13.5.0, there is a new player in town: ARI push configuration.

How does ARI push configuration work?

ARI push configuration uses a subsystem of Asterisk called sorcery. This is the first mention of sorcery on this blog, and I suspect it will not be the last. We’ll save the in-depth discussion of sorcery for a future post. For now, understand that it is a CRUD (create, read, update, delete) API in Asterisk that can read and write to different backends. Sorcery was created for Asterisk 12. Any new modules that require configuration or persistent storage are encouraged to use sorcery. At this time, the only part of Asterisk that uses sorcery for configuration is PJSIP.

ARI has been outfitted with a mechanism to push configuration to sorcery-configured areas of Asterisk. The HTTP PUT, GET, and DELETE commands map to sorcery’s create/update, read, and delete operations. Sending configuration data over HTTP causes sorcery to store the given configuration in the backend. When something internal to Asterisk attempts to retrieve the configuration, sorcery will then retrieve the stored configuration.

Show me, please

The best way to illustrate this is with an example. I have a running Asterisk instance with nothing configured in pjsip.conf

I start with the following sorcery.conf file:

[res_pjsip]
auth = memory

This tells sorcery that objects of type “auth” managed by the “res_pjsip” module should be stored using the “memory” sorcery backend. The memory backend, as the name implies, stores configuration in memory.

I also have the following ari.conf file:

[general]
enabled = yes

[asterisk]
type = user
password = asterisk

This enables ARI and creates an “asterisk” user with password “asterisk”.

Getting configuration

I have the following script that will use ARI to retrieve the “alice” auth object from res_pjsip:

#!/usr/bin/env python
import requests
import json

url = "http://localhost:8088/ari/asterisk/config/dynamic/res_pjsip/auth/alice"

resp = requests.get(url, auth=('asterisk', 'asterisk'))

if resp.status_code == 200:
    print "Received object"
    print json.dumps(resp.json(), sort_keys=True, indent=2,
                     separators=(',', ': '))
else:
    print "Received {0} response".format(resp.status_code)

If you are familiar with Python’s requests library, this should be pretty straightforward. The construction of the URL tells what object type and object name is to be retrieved. The script issues an HTTP GET request to the URL. If the request receives a 200 response, then the script prints the object that was retrieved.

When I run this script right now, it gets a 404 response since the “alice” auth object has not been created. You can see that the “pjsip show auth alice” also shows that the object does not exist.

before push

Pushing Configuration

I have the following python script that will use ARI to push the “alice” auth object to Asterisk.

#!/usr/bin/env python

import requests
import json

url = "http://localhost:8088/ari/asterisk/config/dynamic/res_pjsip/auth/alice"

config = {
    'fields': [
        { 'attribute': 'username', 'value': 'alice' },
        { 'attribute': 'password', 'value': 'supersecret' },
    ]
}

resp = requests.put(url, auth=('asterisk', 'asterisk'), json=config)

if resp.status_code == 200:
    print "Successfully pushed"
    print json.dumps(resp.json(), sort_keys=True, indent=2,
                     separators=(',', ': '))
else:
    print "Received {0} response".format(resp.status_code)

The configuration we are pushing is exactly the same as the following pjsip.conf:

[alice]
type = auth
username = alice
secret = supersecret

When I run the push.py script, you can see that Asterisk tells us what it set for the auth. The “pjsip show auth alice” command now also shows the pushed auth object.

push

Now that the object has been pushed, the get.py script will also now retrieve the object as expected

get after push

Deleting Configuration

I have the following script to delete the object from configuration:

#!/usr/bin/env python

import requests

url = 'http://localhost:8088/ari/asterisk/config/dynamic/res_pjsip/auth/alice'

resp = requests.delete(url, auth=('asterisk', 'asterisk'))

if resp.status_code == 204:
    print "Successfully deleted"
else:
    print "Received {0} response".format(resp.status_code)

When I run this, the object no longer appears when the “pjsip show auth alice” command is run.

delete

Anything else you can show me?

Look at what happens with the configuration when we restart Asterisk.

restart memory

Uh oh! The configuration is gone! This is because the sorcery memory backend gets cleared during an Asterisk shutdown or restart. We can use sorcery to our advantage, though. We’ll alter sorcery.conf a bit:

[res_pjsip]
auth = astdb,dynamic_auth

Sorcery can be told to use the astdb instead of memory. The astdb is a persistent data store and retains its data across restarts and shutdowns. In this case, we’re telling sorcery to store res_pjsip auth objects in the astdb using the prefix “dynamic_auth”. With this change to sorcery, we can push our configuration again and try our previous restart test.

restart astdb

Yay! The configuration is still there! The final CLI command is there just to prove that you can see the object in the astdb.

“But Mark,” you might say, “wouldn’t the astdb be less efficient than memory? Is it possible to somehow have my cake and eat it too?”

Of course there is! Sorcery can be told to use multiple configuration backends, and the order you specify them in sorcery.conf is the order that sorcery uses when trying to find objects. Have a look at this updated sorcery.conf file:

[res_pjsip]
auth/cache = memory
auth = astdb,dynamic_auth

Now we’ve told sorcery to use the memory backend first when trying to find an object. If that fails, then try to find it in the astdb instead. Notice the “/cache” notation. That makes sorcery automatically store a retrieved object in that backend if the object was retrieved from a different backend. In other words, when Asterisk starts up, the “alice” auth object will be in the astdb and not in memory. When something asks for “alice” it will be retrieved from the astdb. Sorcery will then automatically store the object in memory, too. From then on, whenever “alice” is asked for, it will be retrieved directly from memory.

What are the pros and cons of push configuration:

  • Sorcery provides flexibility when deciding how objects are stored/retrieved.
  • Configuration can be updated with no need to reload.
  • Since it uses the REST API, push configuration can be easily integrated into larger Asterisk applications.
  • HTTP uses well known error codes. This makes it easy to troubleshoot
  • The request and response bodies use JSON, which is widely supported by software libraries. JSON also has the benefit of being human-readable.
  • Push configuration only works with sorcery-configured objects.
  • Push configuration may not fit well in deployments that require an application to push configuration to multiple Asterisk servers. Pushing configuration to each Asterisk server is less efficient than writing to a database once.

7 Responses

  1. If push configuration only works with sorcery configured objects, and only PJSIP uses sorcery, it seems of little use.
    I use ARI to play music-on-hold to calls and would really like to be able to dynamically configure new moh classes (upload some audio files to a directory, then use ARI to create a new class to use that directory, and have some calls use that new class). How close is asterisk to allowing that (or can it do it already)?

  2. I know of noone working on such a thing. What most people do is they just reload the configuration I believe.

  3. body:
    {
    “fields”: [
    {
    “attribute”: “username”,
    “value”: “alice”
    },
    {
    “attribute”: “password”,
    “value”: “supersecret”
    }
    ]
    }

    403 Forbidden
    Content-type: application/json
    {
    “message”: “Cannot create sorcery objects of type ‘auth'”
    }

    What is deal here ?

  4. Have you configured sorcery.conf with a backend that allows storage of such things?

  5. This is inside:

    [res_pjsip]
    endpoint=astdb,ps_endpoints
    auth=astdb,ps_auths
    aor=astdb,ps_aors

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.

About the Author

What can we help you find?