FreePBX и динамическая генерация исходящих маршрутов по регионам с использованием базы DEF кодов.
Сегодня у нас под скальпелем оказался asterisk 11.16.0 обёрнутый во FreePBX Distro 12.0.38. У заказчика имеется несколько линий от одного из SIP провайдеров, распределённые по регионам. Логично было бы для снижения затрат на телефонию осуществлять звонки с линии, принадлежащей тому же региону, что и вызываемый абонент. Способ, кстати, подойдёт и в том случае, если вы используете свои физические линии (например, несколько GSM линий с разными ценами за разные направления или для звонков на номера разных сотовых операторов своего региона). И в случае с городскими линиями всё просто — телефонные коды регионов России довольно стабильны и меняются(добавляются) редко. Но что делать с номерами, принадлежащими операторам сотовых сетей? Там один и тот же DEF код может принадлежать разным операторам и делиться по диапазонам номеров. Скажу сразу, в этой статье я не буду рассматривать настройку asterisk для работы с GSM модемом, если интересно — напишу позже.
Поиск решения привёл меня на Хабр, где добрый человек поделился своими соображениями на эту тему и скриптом, эту идею осуществляющим. За что ему большое человеческое спасибо. В двух словах — Федеральное Агентство Связи на своём официальном сайте публикует обновляемый файл, где указана принадлежность DEF кодов и номеров к операторам по регионам. Оригинальный скрипт брал определённый регион и правил маршрут в соответствии с полученными данными. Но у меня несколько направлений и, соответственно, был выбор — запускать несколько скриптов (для каждого из регионов по скрипту) или заняться допилингом. Конечно, я выбрал второй вариант. Был взят за основу скрипт из статьи и несколько переписан для соответствия моим требованиям и большей универсальности. И теперь мне хочется поделиться результатом.
Отличия от оригинального скрипта, доступного по ссылке (ну, кроме исправления опечаток в именах файлов и огрех с использованием статичного имени файла с середины скрипта, при том, что до этого он вызывался через переменную 🙂 ) :
1. Из конфига FreePBX мы вытаскиваем имя и пароль пользователя базы данных (стр. 4..7). Оригинальный скрипт рассчитан на полностью дефолтную установку FreePBX, в которой рутовый пароль к MySQL пустой. Меня, например, не устраивает пустой пароль root, даже когда подключение ограничено localhost-ом и ssh нет ни у кого, кроме меня. Соответственно, в моём случае, оригинальный скрипт не смог бы писать/читать базу. А на тот случай, если мы поменяем пароль для БД — лучше его забирать из конфига, где он однозначно актуален, а не менять его вручную во многих местах.
2. Доп «конфиг. файл». В нём через запятую мы перечисляем имя маршрута во FreePBX и имя региона, ему соответствующего (точные имена регионов смотрим на сайте Россвязи, в файле). Соответственно, за одно выполнение скрипта мы можем править сколько угодно маршрутов. Пример файла:
out-SPb-cell,Ленинградская область out-Msk-cell,Московская область _
Ограничения, налагаемые на конфиг — отсутствие пробелов вокруг запятой и обязательное наличие пустой строки в конце.
3. Переменная LOCCODE (строка 13). В моём случае сотрудники заказчика привыкли набирать номера как придётся — через 7 или через 8. Я прекрасно знаю, что выход на «межгород» у нас осуществляется через восьмёрку или через +, а 7 — код России, и набор номера «79211234567» — жуть и абсурд, но… Но клиент всегда прав :)) . Поэтому появилась эта переменная, формат её — стандартный для asterisk. В моём случае — [78], что означает 7 или 8.
4. Теперь нам не надо знать ID маршрута во FreePBX. В строках 31 и 32 мы выдёргиваем его из базы данных в соответствии с нашим конфигом.
5. Изменён способ применения изменений. В моём случае метод автора с curl-ами не сработал, но прекрасно работает способ из строки 141.
Теперь нам надо добавить запуск скрипта в cron (лучше через полчасика после создания бэкапа) и наслаждаться жизнью. В случае добавления нового направления звонков у заказчика мне достаточно дописать создать и настроить маршрут и дописать одну строчку в конфиг, всё остальное произойдёт само.
Сам скрипт:
#!/bin/bash #Данные для подключения к БД, вытаскиваем из конфига FreePBX tmp_res=$(cat /etc/amportal.conf | grep 'AMPDBUSER') BD_USER=${tmp_res#*=} tmp_res=$(cat /etc/amportal.conf | grep 'AMPDBPASS') BD_PASS=${tmp_res#*=} #Конфиг-файл CONFFILE="./def_trunks.conf" #Временный файл TMPFILE="destinations" #Выход на межгород LOCCODE="[78]" #файл DEF-кодов DOWNFILE='http://www.rossvyaz.ru/docs/num/DEF-9x.html'; #рабочая папка TMPDIR='/tmp/' #файл, где сохраним csv формат кодов FILENAME='codes' #файл, где сохраним паттерны PATTFILE='patterns' #качаем и преобразуем в формат csv /usr/bin/wget -c -q -O - $DOWNFILE | grep "^<tr>" | sed -e 's/<\/td>//g' -e 's/<tr>//g' -e 's/<\/tr>//g' -e 's/[\t]//g' -e 's/^<td>//g' -e 's/<td>/;/g' | iconv -c -f WINDOWS-1251 -t UTF8 > $TMPDIR/$TMPFILE #перебираем конфиг while read -r STR_CNF; do ROUTE_NAME=${STR_CNF%,*} REGION=${STR_CNF#*,} #Выдираем id маршрута из БД FreePBX по имени sql="SELECT route_id FROM outbound_routes WHERE name='$ROUTE_NAME'" ROUTE_ID=$(mysql -u$BD_USER -p$BD_PASS -Dasterisk -sse "$sql") #Выдираем всё, относящееся к текущему маршруту из файла Россвязи /bin/cat $TMPDIR/$TMPFILE | /bin/grep "$REGION" > $TMPDIR/$FILENAME #проверяем не скачали ли пустышку check=/bin/cat $TMPDIR/$FILENAME
#если не пусто - работем if [ "$check" != "" ]; then #скрипт на awk генерации Dial Patterns awk_code=' #функция определения диапазона function ret_diap(from,to) { if ((to-from)==0) return from; else if ((to-from)==9) return "X"; else return "["from"-"to"]"; } #основная функция { DEF=$1; razm=1; delete out_str; for (i=1; i <= length($3);i++) { if ((substr($3,i,1)-substr($2,i,1))==0) { for (r=1; r <= razm;r++) { out_str[r]=out_str[r] substr($3,i,1); } } else { if ((substr($3,i,1)-substr($2,i,1))==9) { for (r=1; r <= razm;r++) { out_str[r]=out_str[r]"X"; } } else { if (substr($3,i,1)-substr($2,i,1)>=1 && substr($3,(i+1),1)-substr($2,(i+1),1)!=9) { count=1; init_str=out_str[1]; for (j=substr($2,(i),1); j < substr($3,(i),1);j++) { if (count==1) { out_str[count]=init_str j ret_diap(substr($2,(i+1),1),9); } else { out_str[count]=init_str ret_diap(j,(substr($3,(i),1)-1)) "X"; j=(substr($3,(i),1)-1); } count++; if (razm<count) razm=count; } out_str[count]=init_str j ret_diap(0,substr($3,(i+1),1)); i++; } else { for (r=1; r <= razm;r++) { out_str[r]=out_str[r]"["substr($2,i,1)"-"substr($3,i,1)"]"; } } } } } for (r in out_str) { print DEF out_str[r]; } }' #исполняем код awk, на выходе - Dial Patterns /bin/cat $TMPDIR/$FILENAME | /bin/awk -F ';' "$awk_code" > $TMPDIR/$PATTFILE #удаляем старые паттерны sql="DELETE FROM outbound_route_patterns WHERE route_id=$ROUTE_ID" /usr/bin/mysql -u$BD_USER -p$BD_PASS -Dasterisk -e "$sql" #формируем новые паттерны sql="INSERT INTO outbound_route_patterns (route_id,match_pattern_pass,match_pattern_prefix) VALUES " n=1 for i in/bin/cat $TMPDIR/$PATTFILE
; do if [ $n -eq 1 ]; then sql="$sql ($ROUTE_ID,'$LOCCODE$i','')" else sql="$sql, ($ROUTE_ID,'$LOCCODE$i','')" fi let n=n+1 done /usr/bin/mysql -u$BD_USER -p$BD_PASS -Dasterisk -e "$sql" fi done < $CONFFILE #Подчищаем за собой /bin/rm -f $TMPDIR/$PATTFILE /bin/rm -f $TMPDIR/$TMPFILE /bin/rm -f $TMPDIR/$FILENAME #Применяем маршруты /var/lib/asterisk/bin/module_admin reload
Здесь его можно скачать.
Комментов пока нет