Недавно мне потребовалось добавить метрику по uptime сервиса дистанционного обслуживания для расчета SLA. Статистика по вызовам API является косвенным показателем работоспособности, а нужна достоверная проверка всех функций от дозвона из внешней сети, до прохождения пользователя по всему меню обслуживания. В интернете ничего готового не видел, поэтому решил поделиться своими изысканиями. Есть система дистанционного обслуживания – клиент может позвонить в call-центр и проверить/изменить настройки своей учётной записи без участия оператора. Для перехода по меню и управления настройками используются тональные сигналы (DTMF). АТС в свою очередь взаимодействует с ядром основной системы через API, возвращая результаты пользователю в виде голосовых сообщений. Задача: настроить автоматизированную проверку системы (правильно отвечает на запросы/выполняет нужные команды).
Главные требования: максимальная правдоподобность имитации пользователя: т.е. нужно именно звонить и нажимать кнопки, а не вызывать методы API в обход call-центра. работа именно с тем планом набора, в который попадают обычные пользователи; нельзя делать специальный контекст для автоматизированной проверки. Для данной статьи упростим наш call-центр и API до безобразия: при звонке в call-центр пользователю доступна единственная услуга (клавиша 1); в ней пользователю предлагается ввести ПИН и в случае его корректности выдается статус учетной записи пользователя (ON/OFF); шаг влево или вправо – выдается сообщение об ошибке. Через API доступно три метода: GET ping (инициализация звонка), GET status (получить статус), POST status (установить статус).  Решать задачу будем с помощью Asterisk. По сути нам нужно собрать аналогичный IVR только от лица клиента: нужно описать машину состояний (ждем приветствие, ждем запроса ПИН и т.д.), и при переходе в каждое из состояний выполнять определённые действия. Команды отправлять понятно как – call-центр ожидает тональные сигналы от пользователя – значит можно воспользоваться командой SendDTMF и «нажимать» нужные кнопки от лица клиента. А как изменять своё состояние? Да точно так же! Для этого немного модернизируем dialplan нашего боевого IVR'а вызовом незамысловатого макроса в ключевых местах: В результате в ключевых местах работы IVR, если звонок поступил от робота, в канал будет отправляться выбранная нами DTMF последовательность. Задержка в 1 секунду добавлена, чтобы наш робот успевал перейти в режим ожидания ввода. Теперь нам пригодится возможность Asterisk отправлять совершаемый звонок в нужный нам локальный контекст – таким образом замкнём между собой IVR call-центра и нашего робота. В самом простом варианте мы можем использовать call-файл и запускать проверку, периодически копируя этот файл в /var/spool/asterisk/outgoing/. Процесс проверки у нас будет такой: 1. Звоним в call-центр 2. ждём, пока можно будет выбирать услугу 3. Нажимаем «1» 4. Ждём, пока можно будет вводить ПИН 5. Вводим ПИН 6. Узнаем состояние — При первой проверке вызовом API меняем состояние на противоположное и заново проверяем состояние (переходим в п.3) — При второй проверке убеждаемся, что состояние изменилось на противоположное 8. Если состояние изменилось, считаем проверку успешной 9. Во всех остальных случаях сообщаем об ошибке Ниже я объединил «на одном экране» dialplan'ы обоих Asterisk'ов и показал, как передается управление/изменение состояний:  Спрятал в спойлер, т.к. рвёт экран. В нашем примере результат выполнения проверки записывается в файл /cc_check.log. В боевой системе вы конечно же эти результаты будете складывать в свою систему мониторинга. В реальной системе простого CURL-запроса к API для проверки всей системы дистанционного обслуживания скорее всего не хватит, поэтому решение можно расширить для контроля звонка робота через AMI. Для этого нужно модифицировать dialplan робота, чтобы он отправлял UserEvent'ы в AMI. Нашу демонстрационную конфигурацию можно изменить следующим образом: Для взаимодействия с таким dialplan'ом необходимо подключиться к Asterisk'у, с которого инициируется звонок робота, через AMI, сделать Originate и дальше действовать в соответствии с приходящими UserEvent'ами. Пример реализации скрипта нашей demo-проверки на Python: Действия выполняются аналогичные описанному выше сценарию с call-файлом, но звонок инициируется по команде скрипта и вызов API тоже осуществляется из скрипта при получении события CC_ROBOT_TOGGLE. Результат проверки приходит вместе с событием CC_ROBOT_RESULT. В итоге мы решили задачу в рамках требуемых условий: звоним и «нажимаем кнопки», делаем проверки в боевом dialplan'е (выдавая тоны по ходу звонка, если звонит робот). Удачного тестирования! Make DevOps a reality