Пишемо скрипти для 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 . Будь-які побажання і критика вітаються.
    
Джерело: Хабрахабр

0 коментарів

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