О чем собственно речь?
Работая в крупной компании, да и в компании среднего размера с небольшим числом филиалов, все чаще задумываешься об оптимизации ресурсов, упрощению администрирования и стандартизации всего и вся.
Есть у нас, к примеру, несколько (ну ладно, не несколько) однотипных филиальчиков на 10-100 сотрудников. Организовать телефонию можно разными путями:
Взять готовое решение в облаке провайдера
Поставить на каждый филиал свой мини-сервер и астериск
Сделать единый астериск в центре
Первые 2 варианта, конечно имеют место быть, но минусов у них хватает. Это и не удобство сопровождения нескольких малых инстантсов, это и финансовый вопрос и некоторые другие моменты.
Если же мы хотим оптимизации, экономии, упростить администрирование, то рано или поздно мы приходим к тому, что сервер в центре будет наиболее удачным решением. Но, как только мы попытаемся стандартизировать номерной план, мы столкнемся с проблемой плоской нумерации. Мы начинаем добавлять префиксы, наращивать длину номеров, и вот у нас уже не 3х значная нумерация, а 6ти значная. Сотрудники все чаще поглядывают косо - чтобы набрать соседа надо нажать 6 кнопок ("6 кнопок Карл! Я сюда не кнопки нажимать пришел!"), а заявление босса что компания еще немного выросла и появился еще один филиал приводит к судорожному перебиранию записей на листочке в поисках свободного префикса в 6ти значной нумерации.
Но как говорится, все уже придумано до нас - это SIP домены. К сожалению, не всегда эта тема так очевидна для администратора-универсала, и порой скрыта в глубинах англоязычных мануалов.
Попробуем разобраться, что это и как это настроить. Прежде всего, домен - это просто текстовая строка, но которую мы можем материализовать в виде днс-имени. А можем и не материализовать и оставить просто строкой. Как правильно? Ну как говорится, все сильно зависит от задачи - работать будет и так и так, но если много софтфонов, то лучше сделать ДНС - на одно поле меньше придется заполнять при настройке.
Тестировать будем астериск 18.1 со стеком PJSIP в связке с реалтайм базой данных.
Что мы будем хранить в базе? Конечно же наших любых юзверей, с их параметрами. Но мы чуть-чуть схитрим и отойдем от мануалов астериска. Не удобно вести те талмуды в базе данных, что предлагается документацией астериска "из коробки".
Поэтому начнем с создания удобной для нас таблички (Numbers), без оглядки на мануалы. И так, что мы должны знать об абоненте?
Конечно же, его номер (Number)
Пароль тоже нужен - вдруг это враг? (Secret)
Домен - ну раз тема про домен, очевидно, он тоже должен быть (domain)
CID - мы ж хотим чтобы он как то отображался при звонке
Опционально: протокол, DTMF, пикап-группа
Поле Flags - свои флаги-опции, которые станут канальными переменными. Адептов полного реалтайм просьба расслабиться, канальные переменные в разы быстрее реалтайма, поэтому такие флаги могут сильно сократить нагрузку на базу данных.
Будем считать, что табличка у нас есть. Что дальше? Ок, такую табличку астериск не прочитает, увы. Но у базы данных есть секретное оружие - вьюшки, старые добрые вьюшки, VIEW. Они позволяют взять данные, в удобном для нас виде, и представить их в удобном виде для астериска. Всего нам потребуется 3 шт:
Endpoints (ps_endpoints)
Авторизация (ps_auths)
Aors (ps_aors)
Это минимальный набор, по вкусу в это блюдо можно добавить и другие, для реалтайма или "волшебства" PJSIP. Позволю себе побыть лентяем и дам просто готовый код SQL:
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW `ps_aors` AS select concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `id`,180 AS `default_expiration`,2 AS `max_contacts`,30 AS `minimum_expiration`,'yes' AS `remove_existing`,'' AS `contact` from `Numbers`;
CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW `ps_auths` AS select concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `id`,'userpass' AS `auth_type`,3600 AS `nonce_lifetime`,`Numbers`.`Secret` AS `password`,`Numbers`.`Number` AS `username`,`Numbers`.`domain` AS `realm` from `Numbers` where `Numbers`.`Secret` <> '';
CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW `ps_endpoints` AS select concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `id`,concat('transport-',lcase(`Numbers`.`Protocol`)) AS `transport`,concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `aors`,if(`Numbers`.`Secret` <> '',concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')),NULL) AS `auth`,'SIP' AS `context`,'all' AS `disallow`,concat('ulaw,alaw,opus',if(find_in_set('Video',`Numbers`.`Flags`),',h263,h261,h263p,h264','')) AS `allow`,concat(`Numbers`.`Number`,if(`Numbers`.`domain` is not null,concat('@',`Numbers`.`domain`),'')) AS `outbound_auth`,concat(`Numbers`.`CID`,' <',`Numbers`.`Number`,'>') AS `callerid`,if(`Numbers`.`Protocol` = 'TLS','sdes','no') AS `media_encryption`,`Numbers`.`PickupGroup` AS `named_pickup_group`,2 AS `device_state_busy_at`,concat('vRecord=',if(find_in_set('Record',`Numbers`.`Flags`) > 0,'yes','no'),';','vRussia=',if(find_in_set('Russia',`Numbers`.`Flags`) > 0,'yes','no'),';','vAbroad=',if(find_in_set('Abroad',`Numbers`.`Flags`) > 0,'yes','no'),';','vVoicemail=',if(find_in_set('Voicemail',`Numbers`.`Flags`) > 0,'yes','no'),';','vFilterCID=no') AS `set_var`,`Numbers`.`NumberID` AS `callerid_tag`,'username,auth_username,ip' AS `identify_by` from `Numbers`;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
Здесь немного поясню. Решил немного усложнить задачу и дополнить условием - одновременно должно существовать два типа нумерации - "глобальная" и "местная":
Глобальная, возьмем что это будет 5ти значные номера. Будем выдавать их в центре и мобильным сотрудникам, которые перемещаются между филиалами. Данные номера уникальны, и не могут повторяться.
Местная, пусть это будет 3х знак, некоторый универсальный номерной план внутри филиала, номера могут повторяться от филиала к филиалу, что позволяет нам сделать стандарт, типа, у директора филиала номер всегда 101, главного бухгалтера 102, зав склада по прозвищу "Вжик" 007 и т.д.
Ок, теперь заполним таблицу Numbers некоторыми тестовыми данными и взглянем на наши вьюшки.
Таблица Numbers:
Отлично, "входная точка для админа" выглядит вполне хорошо, минимум полей, максимум наглядности.
ps_endpoints
Вот он трешак реалтайма астериска, хорошо что это не нужно редактировать - вся табличка сама генерируется на основе таблицы Numbers.
ps_auth
ps_aors
Так, выглядит замечательно. Более того, править будем только таблицу Numbers, поэтому самое время выдохнуть с облегчением.
Думаю многие уже догадались, где скрывается домен. Для тех кто пока еще не понял - посмотрите на столбцы id. Для 5ти знака, там записан просто номер - потому что мы не связываем с каким либо определённым доменом эту нумерацию. А вот для 3х знака мы прописываем после номера через @ домен. Дело в том, что астериск сначала ищет номер и авторизацию с доменом, и если такой в таблице нет - без.
Хочу акцентировать внимание на еще одном моменте. Он не очевиден, и многие спотыкаются и бросают эту тему. Имя AOR для endpointa ДОЛЖНО содержать домен, т.е. быть в формате номер@домен, если номер указан с доменом. Дело в том, что если в номере (id) присутствует домен, то астериск будет искать AOR с доменом. Это захардкорено в код. Еще раз - дать произвольное имя для AOR НЕ получится, это просто не будет работать. Нюанс, однако.
И так, почти все готово, давайте быстро пробежим по настройке связки астериска с базой.
pjsip.conf
Тут давайте немного тормознем, и посмотрим на опции.
disable multi domain
Это опция интересна тем, что по умолчанию она no. Серьезно, чаще всего инсталляции идут без поддержки домена. Но значение по-умолчанию дает астериску команду проверять домены. Если у Вас нет доменов, не поленитесь, поставьте здесь yes. Это сэкономит по 2 обращения на каждую авторизацию и каждый инвайт. База скажет вам спасибо.
endpoint identifier order
Эта опция интересна тем, что правильная ее настройка, экономит нам такты процессора. Значение по умолчанию у ней = ip,username. В такой конфигурации, астериск заточен на работу с транками без авторизации, т.е. когда авторизация идет по IP адресу. Если же у нас высоконагруженный сервер регистрации, мы можем вперед выставить username, что позволит астериску находить нужный endpoint на пару десятков тактов процессора быстрее.
по опциям все, погнали дальше по конфигам.
res config mysql.conf
Тут все очень просто, так что просто листинг - тестовая база стоит локально в этом примере:
extconfig.conf
Тут тоже ничего сложного, просто связываем идентификаторы с таблицами в базе.
ps_contacts можно не связывать, она прекрасно будет себя чувствовать и в astdb.
Spoiler
Наблюдательный читать обратил внимание на некоторый идентификатор CDR. Это один из моих экспериментов, по написанию модуля для выгрузки статистики звонков через механизмы реалтайм. Если заинтересовало, велком на GitHub
sorcery.conf
Мякоть магии реалтайм механизма астериска. Это мой пример, конечно же, конфигурировать нужно исходя из своих потребностей.
Перезапускаем астериск.
Собственно, где результат?
Если все сделано правильно, то у нас должны отобразиться такие endpoint в консоли астериска:
Не буду приводить всю портянку, суть видна на скрине и так. "Глобальные номера" у нас как и обычно без домена. А вот "местная нумерация" вся идет с доменном.
Обращаю внимание, что в диалплане нужно учитывать домен. Т.е., на 5ти знаки:
Dial(PJSIP/19960)
а вот на 3х знаки
Dial(PJSIP/123@test3)
Диалплан получается немного хитрый, но тем не менее, реализовать его не сложно.
Как зарегистрировать абонента?
Microsip, настройки будут выглядеть вот так:
Теперь каждый филиал может иметь свой номер 101 для директора. Разве это не прекрасно?
PS: Диалплан
Первый же вопрос в комментариях - а как звонить между филиалами.
Скажу избитую фразу - диалплан, это кусок творчества, сделать его можно по разному и так как этого требует задача. Поэтому ниже, это лишь пример.
Делаем таблицу domains:
domain (VARCHAR) | code (VARCHAR)
Заполняем, к примеру, вот так:
test2 | 01
test3 | 02
Код филиала можно взять конечно любой - даже 1 знак. Определяемся с выходом на филиалы. Например, у меня, выход на "город" через 9, а на филиалы я возьму 8. Вы берете то, что удобно Вам. Хоть * (звездочка).
Связываем в extconfig.conf таблицу domains с идентификатором domains. Пишем диалплан:
exten => _8XXXXX,1,NoOp(Call from ${CALLERID(all)} to filial ${EXTEN:1:2})
same => n, Set(domain=${REALTIME_FIELD(domains,code,${EXTEN:1:2},domain)})
same => n, GotoIF($[ "${domain}" = "" ]?GotoError) ; Обрабатываем не верный код филиала
same => n, Dial(PJSIP/${EXTEN:3}@${domain})
same => n, Hangip
Итого, набираем на телефоне, 801123 и попадаем в филиал test2 на номер 123.
Для того, чтобы было удобнее администрировать, можно добавить еще ограничение ключа:
Numbers.domain - FOREIGN KEY -> domains.domain
Это позволит не потерять домены при редактировании.
evn
и как звонить? Из филиала одному директору филиала test1 другому директору в филиал test2?
С железного телефона Fanvil X1, к примеру
xomiakba Автор
Добавил в статью ответ.
evn
По сути, мы пришли к 4-значной нумерации.
xomiakba Автор
Нет, не пришли.
Мы пришли к тому, что внутри филиалов 3х значная нумерация, которая никак не связана с глобальной нумерацией, и при внесении каких либо изменений, нам не нужно учитывать общий диалплан системы — 8 как была выходом на другой филиал, так и останется. А вот внутри домена, творим что хотим.
Ничего не мешает, сделать на одном филиале 2х значную нумерацию, на другом 3х значную, например.
Суть доменов в том, чтобы делать отдельную нумерацию внутри домена (не боятся пресечения номеров), и между доменами единая точка переброса звонка по стандартному алгоритму:
123 — позвонить внутри филиала,
8/код филиала/номер в филиале — 8456123 — более наглядно с точки зрения сотрудника, чем:
40123 — мой сосед,
51678 — чувак в филиале в Воронеже.
Мозг привык думать паттернами, и 8 вначале будет выделена им на автомате, в то время каждый раз меняющаяся цифра вначале заставит проводить доп. анализ.
Когда у нас нумерация везде 3х значная это частный случай. Если у вас менее 10 филиалов, разницу, конечно, почувствовать сложно. Но с ростом числа филиалов, с ростом числа сотрудников, с разнородными потребностями внутри филиалов, плоская нумерация очень быстро станет узким местом роста и планирования.
Вот взяли например, 4 знака за основу.
Все было хорошо, пока филиалов не стало 10.
Пришло время менять на 5тизначную.
Надо переделать ВСЕ — справочники, каждый эксель на рабочем ПК, объяснить каждому сотруднику, на сайте не забыть сменить, всем КА раздать новые вн. номера, перенастроить софтфоны/телефоны.
Замечательно, переделали — много труда убили чтобы донести до каждого сотрудника новые правила нумерации. И только все успокоились и привыкли — приходит начальник: мы решили в Спб открыть крупный офис. Там будет 1000 чел.
Ок, мигрируем на 6знак, гемора не меряно, но отважный админ справился с задачей. Теперь у всех 6знак, все софтфоны перенастроены, всем объяснено, все мануалы переделаны, все адресные книги исправлены. И каждый сотрудник набирает уже 6 знаков, даже если ему нужно набрать в свою бухгалтерию в соседней комнате.
И внешним абонентам нужно знать тоже 6ти знак,
потому что в подписи уже портянки: 8 (495) 123 45 45, доб. 123456.
и теперь уже
401123 — чувак в соседнем отделе
543687 — чувак в Воронеже
Да, можно сделать костыли с рваной нумерацией, и на первый взгляд, это решение выглядит не плохо, но только на первый.
С доменами, софтфон, который регистрировался как 123@test2, не требуется перенастраивать, если рядом появился филиал с 4х значной нумерацией. Все что сделано внутри домена, остается внутри домена.
elve
Там скорей всего маршрут с префиксом все ж имеется. Точно также один маршрут на один филиал как и было, но только в рамках одной АТС.