В процесі розробки модуля Ponmon люди часто просили специфічні для них функції, які не входять в стандартний функціонал, а інколи й суперечливі.
Я вирішив, що краще буде розробити розширення для меню, яке буде дозволяти користувачам додавати власні пункти меню, які будуть доступні тільки для них, але зробити їх доступними для інших.
Пропоную обмінюватись такими патчами в цьому репозиторії.
- Хочете додати файл - своріть форк цього репозиторію.
- Кожен функціонал меню повинен бути в окремому файлі, який має починатись з
patch.web.ajOnuMenu. - В назві файлу в дужках вказується вендор, модель та/або тип , а вкінці описується функціонал, наприклад:
(Bdcom_epon)_reboot.pl(ZteC6)_LinkTest.pl
- Якщо вже існує такий файл, але він вам не підходить, то можна додати новий файл з іншим ім'ям в кінці назви.
- В першому рядку функції потрібно вказувати дату останньої зміни файлу. Також можна вказати контакти автора.
- Додавати нові файли можна через створення запиту на злиття.
Модуль Ponmon підтримує розширення функціоналу через додаткові меню для ONU. Ці меню дозволяють виконувати специфічні команди на обладнанні різних вендорів.
Ця документація надає повний огляд системи розширення меню ONU та дозволяє розробникам створювати власні функції для специфічних потреб обладнання.
Система меню працює через хуки - точки розширення в базовому коді web/ajOnuMenu.pl. Кожен модуль-розширення може додавати елементи меню до різних категорій.
| Хук | Призначення | Приклад функцій |
|---|---|---|
menu_bind |
Основні функції прив'язки | Інформація, Link Test |
menu_reboot |
Функції перезавантаження | Reboot ONU, Remove ONU |
menu_settings |
Налаштування портів | Enable/Disable порт |
menu_macs |
Робота з MAC адресами | Перегляд MAC на ONU |
menu_super |
Адміністративні функції | Реконфігурація ONU |
subs |
Додання функцій | Власні процедури |
Кожен patch-файл використовує ACTION директиви для інтеграції з базовим кодом:
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'menu_bind', after=>['Ponmon']
push @menuItems, {
order => 21,
title => L('Інформація'),
act=>'checkInfoOnuBdcom'
} if lc($p{vendor}) =~ 'bdcom' && (lc($p{model}) =~ '33' || lc($p{model}) =~ m/^p36/i);
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'subs', after=>['Ponmon']
sub act_checkInfoOnuBdcom {
# Реалізація функції
}| Параметр | Призначення | Приклад |
|---|---|---|
file |
Цільовий файл для патчування | 'web/ajOnuMenu.pl' |
hook |
Назва хука | 'menu_bind', 'subs' |
after |
Після якого модуля вставляти | ['Ponmon'], 'Ponmon' |
{
order => 21, # Порядок відображення (чим менше - тим вище)
title => L('Назва пункту'), # Локалізована назва
act => 'functionName', # Назва функції-обробника
param => { key => 'value' } # Додаткові параметри (опціонально)
}Меню можуть відображатися залежно від:
# По вендору
if lc($p{vendor}) =~ 'bdcom'
# По моделі
if lc($p{model}) =~ '33'
# По комбінації
if lc($p{vendor}) =~ /^zte/i && $p{model} =~ /c6/i
# З правами доступу
if lc($p{vendor}) =~ 'stels11' && $ses::debugПризначення: Перегляд стану ONU, Link Test, отримання додаткової інформації.
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'menu_bind', after=>['Ponmon']
push @menuItems, {
order => 20,
title => L('Перегляд стану'),
act=>'checkInfoZte'
} if lc($p{vendor}) =~ /^zte/i;
sub act_checkInfoZte {
my $attr = shift;
return $ajax_url->a($lang::btn_Execute, act=>'checkInfoZte', go=>1)
unless ses::input_int('go');
my $tc = _telnetConnect($attr);
ToLog(L('Запросив Detail Info ОНУ sn=[]', $attr->{sn}));
$attr->{info} = _chkInfo($attr, $tc) if $tc;
_telnetClose($tc);
return $attr->{info}{err} if defined $attr->{info}{err};
return $attr->{info}{ok} if defined $attr->{info}{ok};
}Призначення: Перезавантаження, видалення ONU.
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'menu_reboot', after=>['Ponmon']
push @menuItems, {
order => 21,
title => L('Перезавантаження ONU'),
act=>'rebootOnuBdcom'
} if lc($p{vendor}) =~ 'bdcom';
sub act_rebootOnuBdcom {
my $attr = shift;
my $sn = $attr->{sn};
# Підготовка серійного номера для EPON
$sn =~ s/[\:\-\.]//g;
$sn =~ s/(....)(?=.)/$1\./g if $attr->{pon_type} eq 'epon';
return $ajax_url->a($lang::btn_Execute, act=>'rebootOnuBdcom', go=>1)
unless ses::input_int('go');
my $tc = _telnetConnect($attr);
_tnCmd($tc, "\n");
if ($tc->last_prompt() =~ m/\#$/) {
_tnCmd($tc, "epon reboot onu mac-address $sn");
$tc->waitfor('Are you sure to reboot the ONU(y/n)?');
_tnCmd($tc, "y");
$attr->{reconf}{ok} = "Reboot ONU sn=$sn";
} else {
$attr->{reconf}{err} = "not in 'enable' mode";
}
_telnetClose($tc);
return $attr->{reconf}{err} // $attr->{reconf}{ok} // "Backend not defined";
}Призначення: Увімкнення/вимкнення портів, зміна конфігурації.
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'menu_settings', after=>['Ponmon']
push @menuItems, {
order => 21,
title => L('Вкл порт ONU'),
act=>'enableOnuBdcom'
} if lc($p{vendor}) =~ 'bdcom';
sub act_enableOnuBdcom {
my $attr = shift;
return $ajax_url->a($lang::btn_Execute, act=>'enableOnuBdcom', go=>1)
unless ses::input_int('go');
my $tc = _telnetConnect($attr);
if ($tc->last_prompt() =~ m/\#$/) {
_tnCmd($tc, "config");
_tnCmd($tc, "interface $attr->{name}");
_tnCmd($tc, "no shutdown");
_tnCmd($tc, "exit");
_tnCmd($tc, "write");
$attr->{result} = "Port enabled for $attr->{name}";
}
_telnetClose($tc);
return $attr->{result};
}Link Test з параметрами:
sub act_linkTestZteC6 {
my $attr = shift;
my $tests = ses::input_int('tests') || 100;
# Форма для введення параметрів
my $form = _(
'[p][p][p][p]',
$ajax_url->a(L('Історія тестів'), act=>'linkTestHistory'),
L('Кількість тестів'),
v::tag('input', type=>'number', name=>'tests', value=>$tests,
size=>16, min=>1, max=>100, 'required'),
v::submit($lang::btn_Execute)
);
return $ajax_url->form(-class=>'ajax', act=>'linkTestZteC6', go=>1,
-method=>'post', $form) unless ses::input_int('go');
# Виконання тесту
my $tc = _telnetConnect($attr);
# ... логіка тестування ...
# Збереження результатів в БД
Db->do("INSERT INTO `onu_link_test` SET `sn`=?, `tests`=?, `result`=?,
`admin`=?, `time`=UNIX_TIMESTAMP()",
$attr->{sn}, $tests, $result_count, Adm->id);
}sub _telnetConnect {
my $attr = shift;
# Підключення до OLT через Telnet
# Повертає об'єкт з'єднання або undef
}
sub _tnCmd {
my ($tc, $cmd, $options) = @_;
# Виконання команди через Telnet
# Повертає результат виконання
}
sub _telnetClose {
my $tc = shift;
# Закриття Telnet з'єднання
}
sub _tnError {
my ($tc, $msg) = @_;
_telnetClose($tc);
return "Error: $msg";
}# Перевірка режиму enable
if ($tc->last_prompt() =~ m/\#$/) {
# Увійшли в enable режим
}
# Перевірка config режиму
if ($tc->last_prompt() =~ m/\(config\)\#/) {
# Увійшли в config режим
}
# Перевірка interface режиму
if ($tc->last_prompt() =~ m/\(config-if.*\)\#/) {
# Увійшли в режим конфігурації інтерфейсу
}# Для EPON обладнання BDCOM
my $sn = $attr->{sn};
$sn =~ s/[\:\-\.]//g; # Видалення роздільників
$sn =~ s/(....)(?=.)/$1\./g if $attr->{pon_type} eq 'epon'; # Формат XXXX.XXXX.XXXXToLog(L('Запросив Detail Info ОНУ sn=[]', $attr->{sn}));
ToLog(L('Call rebootOnu [] sn=[]', $vendor, $sn));debug('pre', $attr); # Дамп змінної
debug $tc->last_prompt(); # Відладка prompt
debug "check onu"; # Простий текстПриклад SQL схеми для Link Test:
CREATE TABLE IF NOT EXISTS `onu_link_test` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`sn` varchar(32) NOT NULL,
`tests` tinyint UNSIGNED NOT NULL,
`result` tinyint UNSIGNED NOT NULL,
`error` varchar(256) DEFAULT NULL,
`admin` int UNSIGNED NOT NULL,
`time` int UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
KEY `sn` (`sn`)
) ENGINE=InnoDB COMMENT='ONU Link Tests';# Для маленького вікна
ajSmall_window('a_onu_info', $content);
# Для модального вікна
if (ses::input_int('inmodal')) {
unshift @$ses::cmd, {
id => 'modal_window',
data => $content,
};
}my $tbl = tbl->new(-class=>'td_wide pretty');
$tbl->add('*', [
[ '', L('Час'), the_time($time) ],
[ '', L('Результат'), $result ],
[ '', L('Адмін'), $admin_id ],
]);
return $tbl->show;- Завжди перевіряйте вендора та модель
- Використовуйте права доступу для критичних функцій
- Валідуйте всі вхідні дані
- Використовуйте підтвердження для деструктивних операцій
- Логуйте всі дії адміністраторів
- Завжди закривайте Telnet з'єднання
- Перевіряйте prompt'и перед виконанням команд
- Повертайте зрозумілі повідомлення про помилки
- Використовуйте локалізацію L()
- Надавайте зворотний зв'язок про результат операцій
- Створюйте форми для введення параметрів
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'menu_bind', after=>['Ponmon']
push @menuItems, {
order => 21,
title => L('Custom Action'),
act=>'customActionVendor'
} if lc($p{vendor}) =~ 'myvendor';
#<ACTION> file=>'web/ajOnuMenu.pl',hook=>'subs', after=>['Ponmon']
sub act_customActionVendor {
my $attr = shift;
# Форма підтвердження
return $ajax_url->a($lang::btn_Execute, act=>'customActionVendor', go=>1)
unless ses::input_int('go');
# Логування
ToLog(L('Custom action for ONU sn=[]', $attr->{sn}));
# Підключення до OLT
my $tc = _telnetConnect($attr);
return _tnError($tc, 'Connection failed') unless $tc;
# Виконання команд
my $result;
if ($tc->last_prompt() =~ m/\#$/) {
_tnCmd($tc, "show onu-info $attr->{sn}");
$result = "Command executed successfully";
} else {
$result = "Error: Not in enable mode";
}
# Закриття з'єднання
_telnetClose($tc);
return $result;
}