Сначала хочу немного вернуться назад. В первой части я не написал, для чего вообще нужен MIB браузер и сами MIB модули, ведь многие как-то и без всего этого обходятся и вполне себе мониторят свои сети. Как правило для этого используется цифровой OID вида ".1.3.6.1.4.1.171.11.113.1.3.2.2.3" и тематические форумы пестрят запросами "А подскажите OID для того чтобы ...". При желании можно и самому найти нужный OID сделав walk где-то поближе к ветке private.
MIB браузер как раз и является тем инструментом, который позволит Вам упростить работу с оборудованием. Это фактически тоже самое, что иметь иерархическую структуру в базе данных с быстрым и удобным поиском, индексами, типами данных. Вы получаете быстрый доступ ко всем датчикам и триггерам оборудования, с возможностью их создания/правки. Тем самым становится проще администрировать сети, выявлять ошибки и быстрее реагировать на неисправности. Особенно это важно при работе с новыми моделям, ранее неизвестными Вам.
Конечно тут многое зависит от вендоров, вернее от качества предоставляемых ими MIB и реализации snmp на железе. Зачастую в модулях не хватает описаний нужных индексов, индексы не соответствуют реально отдаваемым значениям и т.д. Бывают вообще ситуации за гранью логики, - у меня есть gepon оборудование одного китайского производителя, у которого в ветке enterprises два разных вендора! Вот и попробуй найди нужные OID методом перебора. И не надо писать "вы все быдло, покупайте только циски". Я конечно утрирую, но зачастую комментарии звучат именно так. А циску я как-нибудь в другой раз обосрсужу. Если тема по работе с модулями MIB будет интересна, возможно в будущем я вернусь к ней.
Как-то так. В первой части мы подготовили данные для формирования дерева с помощью jsTree. Загрузим данные(имеющим тонкую душевную организацию, лучше отойти от экранов):
#тут намешан perl, js, html ...
#не нравится? проходи мимо.
sub make_tree {
my $self = shift;
my ( $attr, $id ) = @_;
my $result = '';
#тут определяем как будет работать наше дерево, задаем параметры
#которые потом передадим в JS
my %all;
$all{core}{themes} = ( { variant => 'medium', responsive => 'true' } )
if !$attr->{core}->{themes};
$all{plugins} = ( $attr->{plugins} ) ? $attr->{plugins} : 'search';
$all{search} =
( $attr->{search} )
? $attr->{search}
: ( { case_insensitive => 'true', show_only_matches => 'false' } );
#здесь цепляем хеш с подготовленными ранее данными
%all = ( %all, %$attr );
#перекодируем в JSON
my $DATA = JSON->new->indent->encode( \%all );
#немного подчишаем, возможно есть другой способ
#получить на выходе из json логические операторы без кавычек
$DATA =~ s/"false"/false/g;
$DATA =~ s/"true"/true/g;
$DATA =~ s/\"\*|\*\"/ /g;
$DATA =~ s/"/'/g;
#подключаем стили и библиотеку(хотя никто не мешает
#подключить их в любом другом месте) и готовим DIV для нашего дерева
$result .= qq{
<link rel='stylesheet' href='/styles/lte_adm/plugins/jstree/themes/default/style.min.css' />
<script type='text/javascript' src='/styles/lte_adm/plugins/jstree/jstree.min.js'></script>
<div id='MY_TREE' align='left'></div>
};
#заливаем наши данные в js
$result .= qq(
<script>
jQuery('#MY_TREE').jstree($DATA);
</script>
);
#уфф, всё, рисуем дерево
return $result;
}
Картинка под спойлером:
Ах да, всё это рисуется на каркасе AdminLTE, в теме по-умолчанию, отсюда такие цвета и иконки. На кнопочку "Модули" не обращаем внимания, у Вас её не будет.
Что дальше. Давайте для начала вспомним, как у нас будет выглядеть mib браузер:
это 2 функциональных и один информационный блок:
Непосредственно само дерево, с которым мы будем работать.
Блок данных, возвращаемый устройством. В зависимости от типа данных, можно производить какие-либо манипуляции.
Здесь будет подробная информация о выбранных идентификаторах.
Дерево у нас уже есть. Нарисуем информационный блок:
#в хеше %lang у меня локализация
sub nms_show {
my ($attr) = @_;
$snmpparms{UseSprintValue} = 1;
#я рисую через готовый API, тут уж кто во что
my $table = $html->table(
{
ID => 'SNMP_SHOW',
caption => $label,
}
);
#Набиваем информационный блок по-максимуму, мало ли что в будущем пригодится
$table->addrow( $html->b('objectID'),
$SNMP::MIB{ $attr->{OID} }{objectID} );
$table->addrow( $html->b( $lang{TYPE} ), $SNMP::MIB{ $attr->{OID} }{type} )
if $SNMP::MIB{ $attr->{OID} }{type};
$table->addrow( $html->b('Module'), $SNMP::MIB{ $attr->{OID} }{moduleID} );
$table->addrow( $html->b('Varbinds'),
"@{$SNMP::MIB{ $attr->{OID} }{varbinds}}" ) if $SNMP::MIB{ $attr->{OID} }{varbinds};;
$table->addrow( $html->b( $lang{ACCESS} ),
$SNMP::MIB{ $attr->{OID} }{access} );
$table->addrow( $html->b('Syntax'), $SNMP::MIB{ $attr->{OID} }{syntax} )
if $SNMP::MIB{ $attr->{OID} }{syntax};
$table->addrow(
$html->b( $lang{RANGE} ),
"$SNMP::MIB{$attr->{OID}}{ranges}[0]{low} .. $SNMP::MIB{$attr->{OID}}{ranges}[0]{high}"
) if $SNMP::MIB{ $attr->{OID} }{ranges}[0];
$table->addrow( $html->b( $lang{DESCRIBE} ),
$SNMP::MIB{ $attr->{OID} }{description} )
if $SNMP::MIB{ $attr->{OID} }{description};
$table->addrow( $html->b('Reference'),
$SNMP::MIB{ $attr->{OID} }{reference} )
if $SNMP::MIB{ $attr->{OID} }{reference};
$table->addrow( $html->b('Index(es)'),
"@{$SNMP::MIB{$attr->{OID}}{indexes}}" )
if $SNMP::MIB{ $attr->{OID} }{indexes}[0];
$table->addrow( $html->b('Value List'),
oid_enums( $attr->{OID}, { STR => 1 } ) )
if keys %{ $SNMP::MIB{ $attr->{OID} }{enums} };
return $table->show();
}
#тут разворачиваем идентификаторы имеющие enums
sub oid_enums {
my ( $oid, $attr ) = @_;
my %enums;
my $str = '';
foreach my $el ( keys %{ $SNMP::MIB{$oid}{enums} } ) {
$enums{ $SNMP::MIB{$oid}{enums}{$el} } = $el;
}
if ($attr) {
foreach my $key ( sort { $a <=> $b } keys %enums ) {
$str .= "$key = $enums{$key} </br>";
}
return $str;
}
return %enums;
}
Заглянул в свой код, который отрисовывает 2-й блок, и ужаснулся. У меня там и walk, и get, и gettable, и snmpset. Без поллитры и сам пожалуй не разберусь, поэтому попробую упростить:
#Привожу пример только для snmpget
sub mibs_browser {
my ($attr) = @_;
if ( $FORM{OID} ) {
#Обновляем информацию о идентификаторе
print nms_show( { OID => $FORM{OID} } } );
if ( $FORM{GET} ) {
$snmpparms{UseSprintValue} = 1;
#Будем считать, что в этом месте нам уже известен IP оборудования
#и мы его получаем в хеше $obj
my $sess = SNMP::Session->new( DestHost => $obj->{ip}, %snmpparms );
my $iid = $attr->{IID} || 0;
my $result = $sess->get( [ $FORM{OID}, $iid ] );
#Проверяем на ошибки
if ( $sess->{ErrorNum} ) {
return $html->message( 'err', $lang{ERROR}, $sess->{ErrorStr} );
}
my $result_tbl = $html->table( {} );
$result_tbl->addrow( $html->b( $lang{RESULT} ), $result );
print $result_tbl->show();
return 1;
}
elsif ( ... ) {
#ну и дальше обработчики различных событий
}
#выходим
return 1
}
#создаем блок куда будут отрисовываться 2-й и 3-й блоки
my $res = $html->element('div', '', { id => 'RESULT'});
#Рисуем наше дерево
my $tree = $html->element( 'div', mibs_tree());
#Ну и сваливаем все это в один блок
print $html->element( 'div', $tree . $res, { class => 'row' } );
return 1;
}
Блок 'RESULT
' будет обновляться после нажатия на идентификатор в дереве. Для этого добавим обработчик. JavaScrpt для меня по-прежнему за гранью добра и зла, и если у Вас есть лучшие варианты реализации, - не стесняемся, пишем в комментариях:
//$ID в данном случае уникальный идентификатор,
//благодаря которому мы сможем узнать IP уст-ва
function renewLeftBox(itemName,Action,id,iid){
iid = iid ? iid : 0 ;
var url = 'index.cgi?OID=' + itemName + '&IID=' + iid + '&' + Action + '=$ID';
jQuery('#RESULT').load(url);
};
jQuery('#MY_TREE').on("changed.jstree", function (e, data) {
renewLeftBox(data.instance.get_node(data.selected[0]).id,'SHOW', '$ID')
});
В библиотеке jsTree мы подключили плагин 'contextmenu'.
С его помощью мы будем опрашивать наше оборудование. Еще немного JS:
//Пример мне кажется достаточно простой
//по подобию можно создавать свои менюшки
function customMenu(node) {
var items = {};
//Здесь и далее, я проверяю тип ноды, ну и в зависимости от этого
//возвращаю нужное контекстное меню
if (node.type === 'scalar') {
items.Get = {
label: 'Get',
icon : 'fa fa-angle-down',
action: function () {
renewLeftBox(node.id,'GET');
}
}
}
if (node.type === 'row' || node.type === 'table' || node.type === 'indexes') {
items.Walk = {
label: 'Walk',
icon : 'fa fa-angle-double-down',
action: function () {
renewLeftBox(node.id,'WALK');
}
}
}
if (node.type === 'table') {
items.Table = {
label: 'Table',
icon : 'fa fa-table',
action: function () {
renewLeftBox(node.id,'TABLE');
}
}
}
return items;
}
Пока писал статью, посетила меня шальная мысль, оформить браузер отдельным модулем, чтобы иметь возможность интегрировать его в любой другой проект. Оценил объем работы, попинал свою апатию, - ну его нафиг, не мальчик уже. Но всегда буду рад помочь, тем кто заинтересуется.