Пишем скрипты для Cisco AXL
Фирма, в которой я работаю, для IP-телефонии использует в том числе и Cisco Unified Communications Manager (CUCM). В один прекрасный момент мне понадобилось автоматически отслеживать состояние телефонов —, а именно, зарегистрированы ли они, находятся ли они в Hunt Group и т.п. Несколько часов усиленного гуглежа, собирание скудной информации по кусочкам, и начали появляться более-менее работоспособные скрипты. Ими я и поделюсь в этой статье. Версия моего CUCM — 7.1.5, IP-адрес предполагается 10.0.0.10. Скрипты будут на PHP, но можно запросто переписать на любой другой язык.Первым делом, необходимо включить сервис AXL:1. Переходим в раздел Cisco Unified Serviceability (справа вверху), затем переходим в Tools → Service Activation и включаем Cisco AXL Web Service.2. Убеждаемся, что у нашего пользователя имеется роль Standard AXL API Access.3. Проверить это можно, попробовав открыть страницу https://10.0.0.10:8443/realtimeservice/services/RisPort? wsdl. Если у вас откроется WSDL-документ (XML-документ, описывающий предоставляемые AXL функции), значит, всё хорошо и можно идти дальше.getFunctionsДля взаимодействия с AXL воспользуемся языком PHP и стандартным классом SoapClient. Настроим клиент на вышеупомянутый URL и запросим список имеющихся функций с помощью стандартной функции __getFunctions (): $hostname = '10.0.0.10'; $client = new SoapClient («https://$hostname:8443/realtimeservice/services/RisPort? wsdl», array ('trace'=>true, 'exceptions'=>true, 'location'=>«https://$hostname:8443/realtimeservice/services/RisPort», 'login'=>'LOGIN', 'password'=>'PASSWORD', ));
$response = $client→__getFunctions (); print_r ($response); Получаем прототипы доступных функций: [0] => list (SelectCmDeviceResult $SelectCmDeviceResult, string $StateInfo) SelectCmDevice (string $StateInfo, CmSelectionCriteria $CmSelectionCriteria) [1] => list (string $StateInfo, SelectCtiItemResult $SelectCtiItemResult) SelectCtiItem (string $StateInfo, CtiSelectionCriteria $CtiSelectionCriteria) [2] => ArrayOfColumnValues ExecuteCCMSQLStatement (string $ExecuteSQLInputData, ArrayOfGetColumns $GetColumns) [3] => ArrayOfServerInfo GetServerInfo (ArrayOfHosts $Hosts) [4] => list (SelectCmDeviceResultSIP $SelectCmDeviceResultSIP, string $StateInfo) SelectCmDeviceSIP (string $StateInfo, CmSelectionCriteriaSIP $CmSelectionCriteriaSIP) getServerInfo Самый простой прикладной запрос — информация о сервере: // Здесь и далее описание SOAP-клиента будем опускать
$response = $client→getServerInfo (); print_r ($response); Результат: [HostName] => CUCM1 [os-name] => VOS [os-version] => 2.6.9–78.ELsmp [os-arch] => i386 [java-runtime-version] => 1.6.0_24-b07 [java-vm-vendor] => Sun Microsystems Inc. [call-manager-version] => 7.1.5.33900–10 [Active_Versions] => hwdata-0.146.33.EL-11: … SelectCmDevice Теперь займемся собственно телефонами. Для этого нужно составить SOAP-запрос. К примеру, мы хотим получить информацию о телефонах 501 и 502. Обратите внимание на индексы массива $items: // Тут было описание SOAP-клиента
$items = array (); $items['SelectItem[0]']['Item'] = »501»; $items['SelectItem[1]']['Item'] = »502»;
$response = $client→SelectCmDevice (», array ( «SelectBy» => «DirNumber», // тут можно указать «Name», тогда в массиве $items нужно указывать «SEPaabbccxxyyzz», и т.д. «Status» => «Any», «SelectItems» => $items ));
$devices = $response['SelectCmDeviceResult']→CmNodes[1]→CmDevices; print_r ($devices); Результат [0] => stdClass Object ( [Name] => SEPAABBCC112233 [IpAddress] => 10.0.0.101 [DirNumber] => 501-Registered [Class] => Phone [Model] => 564 [Product] => 451 [BoxProduct] => 0 [Httpd] => Yes [RegistrationAttempts] => 0 [IsCtiControllable] => 1 [LoginUserId] => [Status] => Registered [StatusReason] => 0 [PerfMonObject] => 2 [DChannel] => 0 [Description] => 501 (Ivanov) [H323Trunk] => stdClass Object ( [ConfigName] => [TechPrefix] => [Zone] => [RemoteCmServer1] => [RemoteCmServer2] => [RemoteCmServer3] => [AltGkList] => [ActiveGk] => [CallSignalAddr] => [RasAddr] => )
[TimeStamp] => 1403127103 )
[1] => stdClass Object ( [Name] => SEPAABBCC112234 [IpAddress] => 10.0.0.102 [DirNumber] => 502-Registered [Class] => Phone [Model] => 30016 [Product] => 30041 [BoxProduct] => 0 [Httpd] => Yes [RegistrationAttempts] => 1 [IsCtiControllable] => 1 [LoginUserId] => [Status] => Registered [StatusReason] => 0 [PerfMonObject] => 2 [DChannel] => 0 [Description] => 502 (Petrov) [H323Trunk] => stdClass Object ( [ConfigName] => [TechPrefix] => [Zone] => [RemoteCmServer1] => [RemoteCmServer2] => [RemoteCmServer3] => [AltGkList] => [ActiveGk] => [CallSignalAddr] => [RasAddr] => )
[TimeStamp] => 1403531108 ) PerfmonCollectCounterData В следующем примере будем определять состояние телефонных линий —, а именно, свободны ли линии. Если линия занята (т.е. происходит набор номера либо разговор), то значение равно 1. Если свободна — то 0. Для этого обращаемся к другому WSDL-документу: $hostname = »10.0.0.10»; $client = new SoapClient («https://$hostname:8443/perfmonservice/services/PerfmonPort? wsdl», array ('trace'=>true, 'exceptions'=>true, 'location'=>«https://$hostname:8443/perfmonservice/services/PerfmonPort», 'login'=>'LOGIN', 'password'=>'PASSWORD', ));
$collection = «Cisco Lines»;
$response = $client→PerfmonCollectCounterData ($hostname, $collection); print_r ($response); Результат [0] => stdClass Object ( [Name] => \\10.0.0.10\Cisco Lines (12345678-abcd-dead-beef-0987654321ff:501)\Active [Value] => 0 [CStatus] => 1 )
[1] => stdClass Object ( [Name] => \\10.0.0.10\Cisco Lines (12345678-abcd-dead-beef-0987654321ff:502)\Active [Value] => 1 [CStatus] => 1 ) Тут мы обращаемся к коллекции «Cisco Lines», но можно запросить данные из других коллекций. Список коллекций можно получить с помощью такого запроса: $response = $client→PerfmonListCounter ($hostname); ExecuteCCMSQLStatement Я был немного удивлен, когда узнал, что CUCM поддерживает SQL-запросы к своим внутренним данным. В общем, это тоже возможно. Полный список таблиц можно получить, набрав такую команду в SSH-консоли CUCM: admin: run sql select tabname from systables Галопом по Европам, выполняем запрос, находятся ли телефоны в Hunt Group. Опять же, обратите внимание на структуру массива $items, это довольно неочевидно: $hostname = '10.0.0.10'; $client = new SoapClient («https://$hostname:8443/realtimeservice/services/RisPort? wsdl», array ('trace'=>true, 'exceptions'=>true, 'location'=>«https://$hostname:8443/realtimeservice/services/RisPort», 'login'=>'LOGIN', 'password'=>'PASSWORD', ));
$items = array (); $items[] = array ('Name'=>'hlog'); $items[] = array ('Name'=>'description');
$response = $client→ExecuteCCMSQLStatement («SELECT h.hlog, d.description FROM device AS d INNER JOIN devicehlogdynamic AS h ON d.pkid = h.fkdevice», $items); print_r ($response); Результат [1] => stdClass Object ( [Name] => description [Value] => 501 (Ivanov) )
[2] => stdClass Object ( [Name] => hlog [Value] => t )
[3] => stdClass Object ( [Name] => description [Value] => 502 (Petrov) )
[4] => stdClass Object ( [Name] => hlog [Value] => f ) Таким образом, Иванов находится в Hunt Group (в пуле операторов), а Петров — нет.Веб-приложение для мониторинга Соединив все эти примеры, я получил вполне работоспособное веб-приложение, позволяющее наблюдать за состоянием пула операторов в реальном времени. Если вам интересно, то можно посмотреть исходники на моем GitHub. Любые пожелания и критика приветствуются.