SNMP MIB браузер(продолжение)

от автора

Сначала хочу немного вернуться назад. В первой части я не написал, для чего вообще нужен 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 функциональных и один информационный блок:

  1. Непосредственно само дерево, с которым мы будем работать.

  2. Блок данных, возвращаемый устройством. В зависимости от типа данных, можно производить какие-либо манипуляции.

  3. Здесь будет подробная информация о выбранных идентификаторах.

Дерево у нас уже есть. Нарисуем информационный блок:

#в хеше %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;       }

Пока писал статью, посетила меня шальная мысль, оформить браузер отдельным модулем, чтобы иметь возможность интегрировать его в любой другой проект. Оценил объем работы, попинал свою апатию, — ну его нафиг, не мальчик уже. Но всегда буду рад помочь, тем кто заинтересуется.

ссылка на оригинал статьи https://habr.com/ru/post/560454/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *