Как стать автором
Обновить

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

Время на прочтение6 мин
Количество просмотров11K

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

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

Теги:
Хабы:
Всего голосов 3: ↑3 и ↓0+3
Комментарии3

Публикации

Истории

Ближайшие события