Здравствуй, Хабр!


В процессе своей работы (администрирование активного оборудования) столкнулся с необходимостью оперативного получения онлайн-данных (желательно с графиком) входящего/исходящего трафика на сетевом интерфейсе по SNMP.


При этом всегда попадается такое оборудование, которое либо не заведено в систему мониторинга, либо требует просмотра статистики чаще, чем раз в минуту (как rrdtool).
А в арсенале, зачастую, лишь консоль сервера на Windows или Debian.


Вот тогда и появилась идея сделать небольшую утилитку обладающую следующими возможностями:


— кроссплатформенность;
— без зависимостей (статическая линковка библиотек);
— построение графиков в онлайн-режиме;
— построение графиков в консоли (псевдографика — спасибо, curses);
— шаблоны для специальных OID (пока один для ifInOctets и ifOutOctets);
— возможность прорисовки нескольких кастомных графиков.


Пока альфа-версия бинарников. Разместил здесь на Sourceforge.
Проверено на Windows 7/8/10 32-bit и 64-bit. Debian и Ubuntu.
Кушает данные SNMP — COUNTER, INTEGER, GAUGE.


Примеры запуска утилиты.


Список интерфейсов с OID.


wtraf 10.1.16.2 -l

image


Теперь знаем OID интерфейса (наш №3) и запускаем.


wtraf 10.1.16.2 -i 3

Результат на Windows 8 в небольшом консольном окне:


image


Результат на Ubuntu 18.04 LTS на весь экран:


image


Запускаем с интервалом сбора данных (раз в 5 сек.) и ограничиваем пропускную способность до 50 МБит/сек.


wtraf 10.1.16.2 -i 3 -n 5 -m 50

Результат в PuTTY (прим. — для удобства восприятия график исходящего трафика течет слева, входящего — справа):


image


А теперь самое сочное. Пример кастомных графиков.


wtraf.exe 10.1.16.2 -xc -a .1.3.6.1.2.1.2.2.1.10.2:LAN:rl:x,8,*,1000,/,1000,/:Mbit/s -a .1.3.6.1.2.1.2.2.1.10.3:Internet:bl:x,8,*,1000,/,1000,/:Mbit/s:80 -a .1.3.6.1.2.1.2.2.1.10.4:LAN_to_GUS:gl:x,8,*,1000,/,1000,/:Mbit/s

image


В свою очередь хочу довести до ума утилитку, может порадует админов.


Буду благодарен выявленным ошибкам. На данный момент их очень много, не все залатал.

Комментарии (27)


  1. chemtech
    20.05.2018 21:01

    Спасибо за приложение! Планируется ли переезд на GitHub? (На GitHub легче отправлять pull request)


    1. shprnu Автор
      21.05.2018 05:44

      Да, планируется. Разместил на скорую руку. В том числе планируется выложить исходники.


  1. vetash
    20.05.2018 21:03

    Без исходников?


    1. shprnu Автор
      21.05.2018 05:54

      Будут. Но попозже. Сейчас пытаюсь определить полезную составляющую приложения. Стоит ли развивать.


      1. vetash
        22.05.2018 21:50

        Выложите на гитхаб, по звездам и пулл реквестам можно будет судить о дальнейшем развитии. Да и трекать фичреквесты\баги будет удобнее.


  1. acyp
    21.05.2018 06:20

    del


  1. alex005
    21.05.2018 11:31

    Утилита супер. Автору огромное спасибо! Для себя нашел применение делаешь tmux требуемую раскладку, сохраняешь. В мониторинге делаешь отдельный dashboard и через guacamole выводишь в iframe на этот дашбоард раскладку из tmux (можно сделать одной командой tmux attach) и получаешь графики в реальном времени в мониторинге, очень полезно для оперативной оценки ситуации.


  1. timurnigamov
    21.05.2018 11:57

    И еще не плохо бы видеть статус интерфейса up/down. И чтобы можно было отключать шапку таблицы. Т.е. вывод выглядел бы по маске: ||ifDescription|\n. Было бы совсем круто.


    1. shprnu Автор
      21.05.2018 11:57

      Будет сделано!


    1. alex005
      21.05.2018 12:16

      Ну да, это полезно для dashboard, всегда не хватает места. Подумайте пожалуйста, как можно компактно расположить требуемую информацию

      Мои предложения такие не плохо кроме Description еще и видеть название интерфейса, т.е. например [gigabitethernet 1/0/1]:
      >> Host: ..., Interface:… (up/down speed: 10 Gbit duplex full) Input Errors: и Output Errors: < — перенести в на две строки ниже и добавить скорость и дуплекс
      + interval: 5 sec, max limit: auto < — это вообще убрать
      Outbound traffic: перенести в ту же строку где Max и Min

      Сделать возможность выводить в текстовом виде следующие параметры одной командой
      In_Avg_5: 171
      Input Discards: 0/s
      Input Errors: 0/s
      Input bandwidth: 8.73 bit/s
      Input non-unicast packets: 0/s
      Input unicast packets: 0.02/s
      Length of output queue: 0
      Out_Avg_5: 875
      Output Discards: 0/s
      Output Errors: 0/s
      Output bandwidth: 5.59 Kbit/s
      Output non-unicast packets: 8.78/s
      Output unicast packets: 0/s

      так же не плохо было бы задавать маску для определения состояния, т.е. загружен на 90% например, Warning, и т.п.


      1. alex005
        21.05.2018 12:25

        Пардон, название выводится. Добавлю еще, добавить возможность работать программе в фоновом режиме по заранее определенному файлу конфигурации, если будет маска по требуемому параметру вести (загрузка, ошибки и т.п.) лог, отображающий статус ОK -> WARNING Input bandwidth: 950 Mbit/c или WARNING -> OK.


        1. shprnu Автор
          21.05.2018 17:09

          Концепцию утилиты строил исходя из того, что входные параметры только по аргументам. Выводить информацию в лог, в принципе, можно реализовать.


      1. timurnigamov
        21.05.2018 12:28

        Да и еще обнаружил что ifDescription режется. В варианте без шапки снять ограничение на длину дескприпшена и убрать не значившийся пробелы. Была бы клевая утиля для нагиособразных утилит.


        1. alex005
          21.05.2018 12:38

          Как автору можно перевести donat за программу?
          Да, можно было бы в мониторинг для некоторых интерфейсов отправлять информацию из нее в realtime, чего так не хватает. Например сохранять все опросы за 1 s в rrd файл и выводить их в спец графики.


        1. shprnu Автор
          21.05.2018 13:22

          Понял, рассмотрим. Если что напишу в личку.


          1. alex005
            21.05.2018 14:27

            Вот этим js можно было бы выводить график в realtime
            github.com/terenn/rrdgraph-js


            1. shprnu Автор
              21.05.2018 15:41

              Главный цель программы, выводить графики в консоли без лишних телодвижений. В том числе установки дополнительного ПО и библиотек.


              1. alex005
                21.05.2018 16:22

                Это понятно, я просто написал, как я планирую выводить графики в realtime, если вы сделаете сохранение опросов в rrd/rra и лог файл.


    1. shprnu Автор
      21.05.2018 21:34

      Про up/down хорошо подмечено!


  1. Maxlinus
    21.05.2018 12:18

    Спасибо за программу! проверил с mikrotik работает:)


  1. timurnigamov
    21.05.2018 15:49

    Мне идея утилиты понравилась. Но показалась, что она работает несколько медленно по крайней мере с опцией -l. Я тут пораскинул умишком и на коленках изобразил одну из возможностей Вашей утили на perl. Реализована опция только -l. Представляю на суд общественности свою версию. В моем случае Ваша утилита работает 18 секунд, а моя 4 секунды
    #!/usr/bin/perl -w
    #
    use strict;
    #
    #OIDS
    use Net::SNMP qw(:snmp);
    use Data::Dumper qw(Dumper);
    use vars qw($OURVAR);
    #
    #Debug mode
    my $debug=0;
    my $log_file="/usr/share/cacti/log/test_query_juniper_rsvp_pl.log";
    my $log_open = 0;
    my $verbose=1;
    my $xml_delimiter=':';
    #
    my ($snmp_auth,$snmp_community,$snmp_version,$snmp_port,$timeout,$snmp_user,$snmp_pw);
    my ($result,$session,$error);
    my $retries=1;
    #
    #SNMP OIDS;
    my $ifIndex =".1.3.6.1.2.1.2.2.1.1";
    my $ifStatus = ".1.3.6.1.2.1.2.2.1.8";
    my $ifDescription = ".1.3.6.1.2.1.2.2.1.2";
    my $ifName = ".1.3.6.1.2.1.31.1.1.1.1";
    my $ifAlias = ".1.3.6.1.2.1.31.1.1.1.18";
    my $ifType = ".1.3.6.1.2.1.2.2.1.3";
    my $ifSpeed = ".1.3.6.1.2.1.2.2.1.5";
    my $ifHWaddress = ".1.3.6.1.2.1.2.2.1.6";
    my $ifInOctets = ".1.3.6.1.2.1.2.2.1.10";
    my $ifOutOctets = ".1.3.6.1.2.1.2.2.1.16";
    my $ifOutOctets64 = ".1.3.6.1.2.1.31.1.1.1.10";
    my $FcIdName = ".1.3.6.1.4.1.2636.3.15.3.1.2";
    my $FabricPriority = ".1.3.6.1.4.1.2636.3.15.3.1.3";
    #
    sub usage()
    {
    print «Usage: wtraf.pl -cCommunity -v(1|2) -l ip_device\n»;
    }
    #
    #Main
    #
    #
    my ($command,$i,$option,$ip,$list,$short)=0;
    $snmp_version=1;
    $snmp_community=«public»;
    $timeout=1;

    if ($#ARGV == -1 ){usage();exit;}
    else{$i=0;
    foreach $option (@ARGV)
    {
    chomp ($option);
    if (($option =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) and ($i == $#ARGV)){$ip=$option;print «IPV4: $option\n»;}
    elsif (($option =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) and ($i != $#ARGV)){
    print «Wrong command line ip address must be last.\n»;
    usage; exit;}
    elsif ($option =~ m/\-l/){$command=$option;print «Option: $list\n»;$list=1;}
    elsif ($option =~ m/^\-c\w+?$/) {
    $snmp_community=$option;$snmp_community =~ s/^\-c//;
    print«Community:$snmp_community\n»;}
    elsif ($option =~ m/(^\-v\d$)/) {
    $snmp_version=$option;
    $snmp_version =~ s/^\-v//;
    print«Version:$snmp_version\n»;}
    $i++;
    }
    if ($command eq "-l"){print«List intrefaces:\n»}
    print «Command line options:\nipv4:$ip\ncomunity:$snmp_community\nversion:$snmp_version\n»;
    }
    my %index_table;
    if ($list==1){
    ($session,$error) = Net::SNMP->session(
    -hostname => $ip,
    -community => $snmp_community,
    -version => $snmp_version,
    -timeout => $timeout,
    -retries=> 1,
    -nonblocking => 0,
    );
    if ( !defined $session) { printf «ERROR: %s.\n», $error; exit 1}
    ####ifIndex
    my $request_oid=$ifIndex;
    $result = $session->get_table(
    -baseoid => $request_oid,
    -maxrepetitions => 10,
    );
    my %ifIndex=();
    my %table=%{$result};
    foreach my $key (keys %table)
    {
    my $data= $table{$key};
    if ($key =~ /\.(\d+)$/)
    {$key=$1;}
    $ifIndex{$key}=$data;

    }
    ####ifName
    $request_oid=$ifName;
    $result = $session->get_table(
    -baseoid => $request_oid,
    -maxrepetitions => 10,
    );
    if (!defined $result) { printf «ERROR: %s\n», $session->error(); $session->close(); exit 1; }
    my %ifName=();
    %table=%{$result};
    foreach my $key (keys %table)
    {
    my $data= $table{$key};
    if ($key =~ /\.(\d+)$/)
    {$key=$1;}
    $ifName{$key}=$data;
    }
    ####ifAlias
    $request_oid=$ifAlias;
    $result = $session->get_table(
    -baseoid => $request_oid,
    -maxrepetitions => 10,
    );
    if (!defined $result) { printf «ERROR: %s\n», $session->error(); $session->close(); exit 1; }
    my %ifAlias=();
    %table=%{$result};
    foreach my $key (keys %table)
    {
    my $data= $table{$key};
    if ($key =~ /\.(\d+)$/)
    {$key=$1;}
    $ifAlias{$key}=$data;
    }
    ####ifStatus
    $request_oid=$ifStatus;
    $result = $session->get_table(
    -baseoid => $request_oid,
    -maxrepetitions => 10,
    );
    if (!defined $result) { printf «ERROR: %s\n», $session->error(); $session->close(); exit 1; }
    my %ifStatus=();
    %table=%{$result};
    foreach my $key (keys %table)
    {
    my $data= $table{$key};
    if ($key =~ /\.(\d+)$/)
    {$key=$1;}
    $ifStatus{$key}=$data;

    }
    ###############################
    snmp_dispatcher();
    $session->close();
    my data="";
    foreach my $key (sort keys %ifIndex)
    {
    print "$ifIndex{$key}|$ifName{$key}|$ifAlias{$key}|$ifStatus{$key}\n";
    }
    }
    #End


    1. shprnu Автор
      21.05.2018 16:51

      Спасибо, как раз ранее был написан скрипт на perl.
      Для автономности решено было собрать статические библиотеки net-snmp, pdcurses(ncurses) и boost. И написать утилитку на c++.
      Ввиду сырости программы такая пока скорость, буду улучшать.
      Для сбора информации по интерфейсам использую функцию библиотеки net-snmp:
      «netsnmp_query_walk(hrprload_var, sess_handle);»


      1. shprnu Автор
        21.05.2018 17:00

        Кстати проверил у себя, сканирует в лет — 1 сек.


        1. timurnigamov
          22.05.2018 00:50

          Вероятно у тебя низкий la на сервере.


  1. N0Good
    22.05.2018 09:59

    Эх, ещё бы совместимость с arm…


    1. shprnu Автор
      22.05.2018 10:49

      Поподробней пожалуйста. Может реализуем.


      1. N0Good
        22.05.2018 11:33

        Имеется ввиду возможность сборки (или готовый бинарник) для архитектуры arm, armv7l и т.п.(всевозможные одноплатники). Вероятно, после переезда исходников на github проблем собрать самому не возникнет, но всё же…