Протокол OSSI і Avaya Communication Manager

У даній статті я спробую детально розповісти про використання протоколу OSSI для взаємодії з АТС Avaya Communication Manager. У відкритому доступі дуже мало інформації по даній темі, а вже в російському сегменті все обмежується поверхневої статтею на Хабре за 2013 рік. Необхідно дану усувати несправедливість.

Теорія
Протокол OSSI (Operations Support Systems Interface) використовується в продуктах Avaya для взаємодії різних додаткових модулів з основним модулем АТС, в даному випадку Communication Manager. Отримати доступ до нього можна простим вибором коректного типу терміналу під час підключення до сервера.

Основної уваги заслуговують два типу терміналу: ossi та ossimt. Перший тип використовується для безпосередньої роботи з CM, отриманням інформації та внесенням змін в налаштування АТС. Другий тип використовується для зіставлення ідентифікатора поля, що використовується в першому типі, з фактичним його призначенням. Це потрібно, т. к. в різних версіях CM використовуються різні ідентифікатори і заздалегідь дізнатися що до чого не можна.

Стандартний висновок терміналу ossi:

image

Для цього типу характерно те, що набрані в терміналі символи не відображаються і не видаляються.

Стандартний висновок терміналу ossimt:

image

Взаємодія з протоколом здійснюється шляхом передачі рядків певного типу. Тип рядка визначається покажчиком, подставляемым на початок рядка. Список цих покажчиків:

  • c (command) — покажчик рядка містить виконувану команду;
  • f (field) — покажчик рядка, що містить ідентифікатори полів;
  • d (data) — покажчик рядка, який містить дані, згідно з полем;
  • e (error) — покажчик рядка, що містить повідомлення про помилку;
  • t (terminate) — покажчик закінчення введення/виведення інформації
Введення і висновок інформації в терміналі здійснюється в наступному форматі:

<команда>[RETURN]
 
f<поле 1>[TAB]<поле 2>[TAB]<поле 3>[RETURN]
 
d<дані 1>[TAB]<дані 2>[TAB]<дані 3>[RETURN]
 
t[RETURN]
 
Тобто кожна рядок завершується символом переведення рядка (натиснути Enter), а всередині рядків типу field і data елементи розділяються символом табуляції (натискання Tab).

Розглянемо докладніше перші 3 типи рядків:

c (command)

У даному рядку має міститися команда для виконання. У загальному випадку, команди ідентичні командам, використовуваним при стандартному адміністрування в терміналі. Викликати весь список доступних команд можна набравши:

chelp
 
t
 

f (field)

У цій рядку перераховуються ідентифікатори полів для введеної команди. Поділ між полями здійснюється символом табуляції. Дані записані ідентифікатори в HEX-форматі і щоб зрозуміти яке поле чому відповідає, необхідно звертатися до ossimt. Рядків даного типу може бути декілька, в залежності від кількості інформації, що передається командою.

d (data)

У цій рядку перераховуються дані, що стосуються відповідних полів, зазначеним у рядку попереднього типу. Дані також поділяються табуляцією. Слід враховувати, що кількість рядків даного типу має бути кратно кількості рядків типу field.

Рядки типу field та data в основному використовуються при виведенні інформації після введення команди, але крім цього їх можна використовувати при внесенні змін в систему (напр. change station XXXX), або виведення команди, що приймає додаткові параметри (напр. display alarms). Досить тільки після рядка command додати ці рядки, вказавши в них відповідні поля і дані, які потрібно змінити.

Наприклад, набравши в терміналі:

ссһа st 1000
 
f8003ff00
 
dI.C. Wiener
 
t
 
Ми змінимо для внутрішнього номери 1000 коротке ім'я на I. C. Wiener.

Строго кажучи, терміналів типу ossi кілька. Мені відомо принаймні 3: ossi, ossi3, ossis. Особливої різниці між ними немає. З видимих особливостей тільки те, що ossis при виведенні результату команди не повертає рядок з самою командою.

Практика
Все описане, звичайно, добре. Але як можна використовувати цей протокол на благо? Ну, наприклад, можна зробити якусь подобу моніторингу. Розглянемо для прикладу моніторинг медіа-шлюзів.

В цьому нам допоможе звичайна команда status media-gateways і Python.

Крок 1: підключитися і отримати інформацію.

import telnetlib

tn = telnetlib.Telnet('127.0.0.1', '5023') # Підключаємося по телнету на стандартний порт 5023
tn.read_until('login'.encode()) # Чекаємо приходу рядки з введенням логіну
tn.write('username\n'.encode())
tn.read_until('Password'.encode()) # Чекаємо приходу рядки з введенням пароля
tn.write('password\n'.encode())
tn.read_until('Pin'.encode()) # Чекаємо приходу рядки з введенням пін-коду
tn.write('pin\n'.encode())
tn.read_until('Terminal'.encode()) # Чекаємо приходу рядки з введенням типу терміналу
tn.write('ossi\n'.encode())
tn.read_until ('\n'.encode()) # Чекаємо приходу рядки з ідентифікатором закінчення вводу\виводу

# Як тільки ідентифікатор прийшов, можна слати команду
tn.write('csta media-g\n'.encode()) # Рядок типу command
tn.write ('\n'.encode()) # Рядок типу terminate
output = tn.read_until ('\n'.encode()) # Записуємо всю інформацію, поки не прийде terminate.
output = output.decode('utf-8') # Конвертуємо прийшли байти в рядок

Тепер у нас є інформація у вигляді рядка, з якою ми можемо творити всяке.

output
'\ncsta media-g\nf6c02ff00\t6c08ff00\t6c0aff00\t6c0cff00\t6c03ff00\nf6c09ff00\t6c0bff00\t6c04ff00\t6c0fff01\t6c0fff02\nf6c0fff03\t6c0fff04\t6c0fff05\t6c0fff06\t6c0fff07\nf6c0fff08\t6c10ff09\t6c10ff0a\t6c10ff0b\t6c10ff0c\nf6c10ff0d\t6c10ff0e\t6c10ff0f\t6c10ff10\t6c11ff11\nf6c11ff12\t6c11ff13\t6c11ff14\t6c11ff15\t6c11ff16\nf6c11ff17\t6c11ff18\t6c12ff19\t6c12ff1a\t6c12ff1b\nf6c12ff1c\t6c12ff1d\t6c12ff1e\t6c12ff1f\t6c12ff20\nf6c13ff21\t6c13ff22\t6c13ff23\t6c13ff24\t6c13ff25\nf6c13ff26\t6c13ff27\t6c13ff28\nd0\t0\t0\t01\t0\nd0\t26\t40\t2 0| 0| 3|up\t5 0| 0| 2|up\nd8 0| 0| 3|up\t9 0| 0| 5|up\t10 0| 0| 3|up\t11 0| 0| 3|up\t12 0| 0| 1|up\nd13 0| 0| 0|up\t14 0| 0| 1|up\t15 0| 0| 0|up\t16 0| 0| 3|up\t180| 0| 0|up\nd19 0| 0| 0|up\t21 0| 0| 0|up\t22 0| 0| 0|up\t23 0| 0| 4|up\t24 0| 0| 1|up\nd25 0| 0| 1|up\t26 0| 0| 0|up\t27 0| 0| 0|up\t28 0| 0| 1|up\t29 0| 0| 1|up\nd30 0| 0| 1|up\t33 0| 0| 1|up\t34 0| 0| 5|up\t37 0| 0| 1|up\t\nd\t\t\t\t\nd\t\t\t\t\nd\t\t\nt\n'


Крок 2: парсим отриману інформацію.

Наявну інформацію нам потрібно розділити залежно від означених вище типів рядків. Оскільки кількість рядків одного типу може бути кілька, нам необхідно врахувати їх послідовність.

fields = {} # словник для зберігання полів
data = {} # словник для зберігання даних
lines = output.split('\n') # поділяємо порядково
for line in lines: # проходимся по рядках і заносимо у відповідний словник
if line.startswith('d'): # рядок типу data
data.update({
len(data): line[1:] # Ключем словника робимо номер рядка
})
elif line.startswith('f'): # рядок типу field
fields.update({
len(fields): line[1:]
})
elif line.startswith('t'): # рядок terminate
break
else: # інші типи рядків нам не цікаві
pass
parse = {
'fields': fields,
'data': data,
}

В результаті цих дій отримаємо таку змінну parse:

parse
{
'fields': {
0: '6c02ff00\t6c08ff00\t6c0aff00\t6c0cff00\t6c03ff00', 
1: '6c09ff00\t6c0bff00\t6c04ff00\t6c0fff01\t6c0fff02', 
2: '6c0fff03\t6c0fff04\t6c0fff05\t6c0fff06\t6c0fff07', 
3: '6c0fff08\t6c10ff09\t6c10ff0a\t6c10ff0b\t6c10ff0c', 
4: '6c10ff0d\t6c10ff0e\t6c10ff0f\t6c10ff10\t6c11ff11', 
5: '6c11ff12\t6c11ff13\t6c11ff14\t6c11ff15\t6c11ff16', 
6: '6c11ff17\t6c11ff18\t6c12ff19\t6c12ff1a\t6c12ff1b', 
7: '6c12ff1c\t6c12ff1d\t6c12ff1e\t6c12ff1f\t6c12ff20', 
8: '6c13ff21\t6c13ff22\t6c13ff23\t6c13ff24\t6c13ff25', 
9: '6c13ff26\t6c13ff27\t6c13ff28'
},
'data': {
0: '0\t0\t0\t01\t0', 
1: '0\t26\t40\t2 0| 0| 3|up\t5 0| 0| 2|up', 
2: '8 0| 0| 3|up\t9 0/0| 5|up\t10 0| 0| 3|up\t11 0| 0| 3|up\t12 0| 0| 1|up', 
3: '13 0| 0| 0|up\t14 0| 0| 1|up\t15 0| 0| 0|up\t16 0| 0| 3|up\t18 0| 0| 0|up', 
4: '19 0| 0| 0|up\t21 0| 0| 0|up\t22 0| 0| 0|up\t23 0| 0| 4|up\t24 0| 0| 1|up', 
5: '25 0| 0| 1|up\t26 0| 0| 0|up\t27 0| 0| 0|up\t28 0| 0| 1|up\t29 0| 0/1|up', 
6: '30 0| 0| 1|up\t33 0| 0| 1|up\t34 0| 0| 5|up\t37 0| 0| 1|up\t', 
7: '\t\t\t\t', 
8: '\t\t\t\t', 
9: '\t\t'
}
}


Крок 3: зіставляємо поля і дані.

Нарешті, нам потрібно зіставити ідентифікатор поля конкретного значення з даних.

result = {} # Сюди будемо зберігати результат
for i in range(len(parse['fields'])): # вважаємо кількість рядків і проходимся по ним
fids = parse['fields'][i].split('\t') # поділяємо рядок на елементи
data = parse['data'][i].split('\t')
for i in range(len(fids)):
result.update({
fids[i]: data[i] # зіставляємо поле відповідним даним
})

У підсумку отримуємо словник, в якому в якості ключа варто ідентифікатор поля, а значення — дані, відповідні поля.

result
{
'6c10ff0e': '21 0| 0| 0|up', 
'6c11ff16': '29 0| 0| 1|up', 
'6c13ff22': ", 
'6c0fff01': '2 0| 0| 3|up', 
'6c10ff0c': '18 0| 0| 0|up', 
'6c11ff15': '28 0| 0| 1|up', 
'6c10ff0d': '19 0| 0| 0|up', 
'6c12ff20': ", 
'6c10ff09': '14 0| 0| 1|up', 
'6c0fff03': '8 0| 0| 3|up', 
'6c10ff0f': '22 0| 0| 0|up', 
'6c11ff14': '27 0| 0| 0|up', 
'6c04ff00': '40', 
'6c13ff26': ", 
'6c10ff0b': '16 0| 0| 3|up', 
'6c10ff0a': '15 0| 0| 0|up', 
'6c0fff08': '13 0| 0| 0|up', 
'6c13ff25': ", 
'6c0cff00': '01', 
'6c12ff1f': ", 
'6c11ff18': '33 0| 0| 1|up', 
'6c13ff27': ", 
'6c11ff12': '25 0| 0| 1|up', 
'6c0fff06': '11 0| 0| 3|up', 
'6c0bff00': '26', 
'6c03ff00': '0', 
'6c11ff11': '24 0| 0| 1|up', 
'6c0aff00': '0', 
'6c10ff10': '23 0| 0| 4|up', 
'6c13ff28': ", 
'6c0fff07': '12 0| 0| 1|up', 
'6c12ff1b': ", 
'6c02ff00': '0', 
'6c0fff05': '10 0| 0| 3|up', 
'6c13ff23': ", 
'6c12ff1e': ", 
'6c08ff00': '0', 
'6c12ff1d': ", 
'6c12ff1a': '37 0| 0| 1|up', 
'6c11ff13': '26 0| 0| 0|up', 
'6c12ff1c': ", '6c13ff24': ", 
'6c13ff21': ", 
'6c0fff02': '5 0| 0| 2|up', 
'6c09ff00': '0', 
'6c12ff19': '34 0| 0| 5|up', 
'6c0fff04': '9 0| 0| 5|up', 
'6c11ff17': '30 0| 0| 1|up'
}


Фінал: PROFIT

Отже, у нас є словник, в якому є вся інформація по статусу медіа-шлюзів. Нам залишається тільки з'ясувати який ідентифікатор що позначає. Робиться це, як ми пам'ятаємо, з допомогою ossimt. Наприклад, для полів major alarms, minor alarms, warnings відповідають ідентифікатори 6c02ff00, 6c03ff00, 6c04ff00. Шукаємо їх у нашому словнику, і розуміємо, що у нас немає жодної серйозної помилки і «всього» 40 попереджень. Жити можна.

Трохи попрацювавши з отриманими даними, можна одержати цілком придатний моніторинг медіа-шлюзів доступний прямо з терміналу. Наприклад, ми можемо отримати таку картину:

image

А якщо не пощастить, то й таку:

image

Для зручності роботи, розробив невеликий клас для роботи з даним протоколом, подивитися можна тут.
Джерело: Хабрахабр

0 коментарів

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