Огляд Asterisk REST Interface (ARI)

На початку часів єдиним "постачальником" функціоналу Asterisk були модулі, багато з яких розширювали арсенал додатків і функцій плану набору.
Тоді, на початку часів, всі ці команди і функції далеко випереджали свій час, і завдяки їм Asterisk "уделывал" по функціоналу багато комерційні продукти.
Якщо виникала якась необхідність у виході за межі наявних додатків і функцій, можна було написати свій власний модуль на мові С, і це був єдиний спосіб розширення функціоналу і виходу з наявної "клітини", якою б просторою вона не була.
Але розробку модуля Астеріск на мові С складно назвати тревиальной завданням. Це досить тернистий шлях, до того ж вельми ризикований, адже критична помилка у своєму модулі запросто призводила до повного падіння Asterisk core.
Потрібні були більш "м'які" і прості способи для розширення функцій і інтеграції з іншими системами.
Так з'явилися інтерфейси AGI і AMI.

Asterisk Gateway Interface (AGI) — це синхронний інтерфейс виконання диалплана, архітектурно "слизанный" з CGI. Команда диалплана AGI запускала процес, і використовувала стандартний ввід і вивід для отримання команд і передачі результатів. За допомогою AGI можна вирішувати завдання інтеграції з зовнішніми системами, наприклад, можна відправитися в корпоративну базу даних і знайти ім'я абонента клієнта за його номером.
По суті, AGI надавав спосіб написати план набору Asterisk не у форматі extensions.conf, а на своїй мові програмування, використовуючи поставляються модулями команди і функції, навколо яких будується своя бізнес-логіка.
Asterisk Interface Manager (AMI) — це асинхронний (подієвий) інтерфейс, що дозволяє контролювати внутрішній стан об'єктів в Asterisk, і отримувати інформацію про події, що відбуваються. Якщо AGI архітектурно нагадує інтерфейс CGI, то AMI сесія схожа на телнет-сесію, в рамках якої стороннє додаток підключається по TCP/IP до AMI порту Asterisk, і може відправляти свої команди, відповідь на які приходить через деякий час у вигляді події-відповіді. Крім відповідей на команди AMI з'єднання "валяться" всілякі події, що відбуваються в Asterisk, і справа клієнта визначити, чи відносяться вони до нього або їх можна просто ігнорувати.
Про AGI можна сказати, що це call execution механізм, а про AMI — що це call control механізм. Найчастіше для побудови свого телекомунікаційного програми необхідно використовувати відразу AGI і AMI разом. Відбувається "розмазування" бізнес логіки за різними додатками, що ускладнює його розуміння і подальший супровід і розвиток.
Крім цього, існує ще кілька обмежень:
  • AGI: блокує потік, обслуговуючий канал.
  • AGI: реакція на події (DTMF, зміна стану) неможлива або утруднена тільки з AGI.
  • Фундаментальні операції обмежені тим, що виконується на каналі. Але є й інші примітиви: мости, пристрої, стану, індикації повідомлень і медіа на каналах, недоступні в AGI/AMI.
  • AMI & AGI — морально застаріли. REST, XML/JSON-RPC більш звичні і зручні в сьогоднішньому світі.
У результаті, щоб вирватися за рамки існуючих обмежень команд і функцій, треба і писати свій З-модуль, що реалізує низькорівневий телефонний примітив, і інтегруватися з зовнішніми системами за допомогою AGI & AMI.
Так було до появи Asterisk REST Interface.
Основні концепції ARI:
  • ARI дозволяє керувати станом дзвінка (call control), так і виконувати логіку (call execution).
  • ARI асинхронен.
  • ARI «виставляє» «сирі» примітиви — канали, мости, пристрої і т. п. через REST інтерфейс.
  • Стану об'єктів доступні через JSON події поверх WebSocket.
  • ARI — не для того, щоб «зарулити» дзвінок у додаток VoiceMail, а для того, щоб створити своє власне додаток VoiceMail!
"Три кити" ARI:
  • RESTful інтерфейс.
  • WebSocket підключення, по якому передаються події контрольованих ресурсах у форматі JSON.
  • Додаток диалплана — Stasis, що передає керування каналом в ARI додаток.
Приклад диалплана, передає управління у Stais:
розширеннями => _X.,1,Stasis(myapp,arg1,arg2)
розширеннями => _X.,n,NoOp(Left Stasis)

ARI має деякі обмеження
  • ARI не має доступу до будь-яких об'єктів, а тільки до тих, які контролює. Це означає, що не можна зробити answer на каналі, які не зарулен в Stasis додаток. Однак, channel list поверне всі активні канали, а не тільки ті, що зарулены в Stasis
  • Доступні тільки ті операції, які визначені на стороні Asterisk (що зрозуміло, адже це Asterisk визначає всі REST операції).
  • Stasis додаток доступний тільки при встановленому клієнтському з'єднанні. Якщо немає з'єднання на WebSocket з іменем даного додатка, Stasis видасть помилку і піде далі диалплану.
Розглянемо категорії операцій, доступних в ARI:
  • Asterisk
  • Мости (bridges)
  • Канали (channels)
  • Пристрою (endpoints)
  • Стану пристроїв (device states)
  • Події (events)
  • Поштові скриньки (mailboxes)
  • Відтворення (playbacks)
  • Записи (recordings)
  • Звуки (звуки)
І зупинимося на кожній категорії детальніше.
Asterisk
  • Динамічна конфігурація (sorcery, pjsip)
  • Інформація про збірку
  • Керування модулями (список, завантаження, вивантаження)
  • Управління логированием і ротацією логів
  • Глобальні змінні (читання і установка)
Мости
  • Отримання, створення, видалення мостів
  • Додавання / видалення каналів
  • Програвання музики на очікуванні
  • Включення запису
Канали
  • Список активних каналів і докладні дані каналу.
  • Створення каналу (originate) та видалення (hangup) каналу.
  • Вихід в диалплан
  • Редирект каналу
  • Answer, Ring, DTMF, Mute, Hold, MoH, Silence, Play, Record, Variable, Snoop
Канали
  • Список активних каналів і докладні дані каналу.
  • Створення каналу (originate) та видалення (hangup) каналу.
  • Вихід в диалплан
  • Редирект каналу
  • Answer, Ring, DTMF, Mute, Hold, MoH, Silence, Play, Record, Variable, Snoop
Пристрою
  • Список всіх пристроїв
  • Надіслати повідомлення на пристрій (SIP, PJSIP, XMPP)
Стан пристроїв
  • Список статусів контрольованих пристроїв
  • Встановлення статусу (NOT_INUSE, INUSE, BUSY, INVALID, UNAVAILABLE, RINGING, RINGINUSE, ONHOLD)
Повний список можливих операцій дивіться на wiki asterisk — https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+ARI
Події
Наведу частковий список подій, які доступні на веб-сокеті підключеного програми:
  • StasisStart / StasisEnd — надсилається в сокет відразу при попаданні дзвінка в Stasis, і останнім при виході дзвінка з Стасиса.
  • ChannelCreated / ChannelDestroyed — при створенні та руйнуванні каналу.
  • BridgeCreated / BridgeDestroyed — при створенні та руйнування мосту.
  • ChannelDtmfReceived — при отриманні DTMF.
  • ChannelStateChange — змінився стан каналу.
  • ChannelUserevent — користувальницьке подія. Дуже зручна штука, яка дозволяє надбудовуватися над подієвої моделлю ARI.
  • DeviceStateChanged — змінився стан пристрою (NOT_INUSE, INUSE, BUSY, INVALID, UNAVAILABLE, RINGING, RINGINUSE, ONHOLD).
  • EndpointStateChange — змінився стан кінцевої точки.
  • PlaybackStarted / PlaybackFinished — почалося і закінчилося програвання файлу.
  • TextMessageReceived — отримано повідомлення.
  • іншіhttps://wiki.asterisk.org/wiki/display/AST/Asterisk+13+REST+Data+Models)
Що нового в Asterisk 14 ARI
  • Отримання записів
  • Програвання медіа з HTTP джерел.
  • Медіа-плейлист (асинхронність вимагала очікування закінчення одного звуку для запуску наступного).
Приклад
Ну і на закінчення наведу приклад оригинации виклику за допомогою Python ARI бібліотеки.
У цьому прикладі робиться оригинация за вказаною бенкеті, і повертається cause code:
import ari
import gevent
from gevent.event import Event
from gevent.monkey import patch_all; patch_all()

ari_client = ari.connect(
conf['pbx_ari_url'],
conf['pbx_ari_user'],
conf['pbx_ari_password']
)

def originate_to_ari(endpoint=", number=", app=", variables={},
callerid=", timeout=60):

evt = Event() # Wait flag for origination
result = {}
channel = ari_client.channels.originate(
endpoint=endpoint,
app='barrier',
appArgs=app,
callerId=callerid,
timeout=timeout,
variables={'variables': variables},
)

def destroyed(channel, event):
result['status'] = 'success'
result['message'] = '%s (%s)' % (
event.get('cause_txt'),
event.get('cause'))
evt.set()

channel.on_event('ChannelDestroyed', destroyed)
# Wait until we get origination result
evt.wait()
return result

P. S. Написано за матеріалами виступу автора на Asterconf 2016.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.