Perl/Net::SNMP: скрипт слишком медленный, нужно оптимизировать


Я хотел бы оптимизировать свой perl-скрипт, потому что он немного медлителен для отображения информации о сети.
Я не знаю, что можно изменить или улучшить, чтобы ускорить выполнение скрипта.
Я манипулирую несколькими хэши, чтобы получить: mac add, index и т. д. Я думаю, что это немного тяжело, но другого выбора нет.
Кроме того, я делаю много запросов SNMP, и обработка ошибок, возможно, не очень хороша.
Я копирую/вставляю свой скрипт и его модуль.
Заранее спасибо за чтение моего кода.

Он принимает аргументы:

  1. имя интерфейса (например, FastEthernet0/9 или FastEthernet0/1...)
  2. hostname : ip коммутатора
  3. сообщество (часто = общественное)

Надеюсь, это понятно.

 #!/usr/bin/perl
    use strict;
    use warnings;
    use Getopt::Long;
    use SnmpUtil;
    use AdresseMac;
    use Net::SNMP;
    use Net::SNMP::Interfaces;

    my $ifname;
    my $hostname;
    my $community;
    my $version = 1;

    GetOptions( "ifname=s"  => \$ifname,
                "host=s"    => \$hostname,
                "community=s"   => \$community,
                "protocol:s"    => \$version);

    my $interfaces = Net::SNMP::Interfaces->new(Hostname => $hostname, Community => $community);
    my $inter = $interfaces->interface($ifname);
    #Get interface $ifname
    my $ifindex = $inter->index();
    #Vitesse
    my $vitesse = $inter->ifHighSpeed();
    #Alias
    my $ifalias = $inter->ifAlias();

    #Seek for VLANs
    my $vlan_trouve;

    #Listing all VLANS
    my $vmVlan = "1.3.6.1.4.1.9.9.68.1.2.2.1.2"; #OID of vlan table
    my $vlans = SnmpUtil->new($hostname, $community);
    my %vl = $vlans->requeteTable($vmVlan);

    $vlans->deconnexion();

    #Get the good VLAN corresponding to index interface
    $vlan_trouve = $vl{$ifindex};

    #Listing : port VLAN <-> @mac
    my $dot1dTpFdbAddress = "1.3.6.1.2.1.17.4.3.1.1";
    my $dot = SnmpUtil->new($hostname, $community."@".$vlan_trouve);
    my %dot1address = $dot->requeteTable($dot1dTpFdbAddress);

    #Listing : numPortBridge <-> port VLAN
    my $dot1dTpFdbPort = "1.3.6.1.2.1.17.4.3.1.2";
    my %portdot = reverse($dot->requeteTable($dot1dTpFdbPort));

    #Listing : num Port bridge <-> ID port switch
    my $dot1dBasePortIfIndex = "1.3.6.1.2.1.17.1.4.1.2";
    my %dotindex = reverse($dot->requeteTable($dot1dBasePortIfIndex));

    #Duplex (auto, half or full)
    my $oid_cisco_duplex = "1.3.6.1.2.1.10.7.2.1.19.".$ifindex;
    my $duplex = $dot->requete($oid_cisco_duplex);
    if ($duplex==1) {
        $duplex= "Auto";
    }
    elsif ($duplex==2) {
        $duplex = "Half";
    }
    elsif ($duplex==3) {
        $duplex= "Full";
    }
    #Close connection
    $dot->deconnexion();

    #Go back up, to find mac add
    my $numportbridge = $dotindex{$ifindex};
    if (!defined($numportbridge)) {
        print "Erreur : $ifindex not found in list : num Port bridge <-> ID port switch\n";
        exit 2;
    }
    my $portVlan = $portdot{$numportbridge};
    if (!defined($portVlan)) {
        print "Erreur : $numportbridge not found in list : numPortBridge <-> ports du VLAN\n";
        exit 3;
    }
    my $add = uc($dot1address{$portVlan});
    if (!defined($add)) {
        print "Erreur : $portVlan not found in list : ports du VLAN <-> \@mac\n";
        exit 4;
    }
    $add =~ s/^0X//g;
    printf "<b>Port : $ifname</b><br/>Index $ifindex on VLAN : $vlan_trouve<br/>\@mac : $add<br/>Speed=$vitesse Mbps Alias=$ifalias<br/>Duplex: $duplex\n";

Вот SnmpUtil.pm:

#!/usr/bin/perl
use strict;
use warnings;
use Net::SNMP;
package SnmpUtil;

our ($session, $error);

sub new {
  my ($classe, $hostname, $community) = @_;
  my $this = {
    "hostname"  => $hostname,
    "community" => $community
  };
  bless($this, $classe);
  $this->{connexion} = $this->connexion;
  return $this;
}

sub connexion {
    my ($this) = @_;

    ($session, $error) = Net::SNMP->session(
        -hostname  => $this->{hostname},
        -community => $this->{community},
        -version   => "1",
        -timeout   => 3,
    );
    request_error_connexion() if (!defined($session));
}

sub request_error_connexion {   
    my ($this) = @_;
    print "Erreur : can't connect to host\n";
    print "Erreur : $error\n";
    if ($error =~ /The argument "-community" is unknown/)
        {
                # protocol SNMP version 3 not working 
                exit 3;  # code ret final = 3*256 = 768
        }
    else
    {
        exit 1; # code retour final = 1*256 = 256
    }
}

sub request_error {
        my ($this) = @_;
        print "Error : no answer from host\n";
        printf "Erreur : %s\n",$session->error;
        if ($session->error =~ /No response from remote host/)
        {
                #host ok, bad community or host refuse connection
                $session->close;
                exit 2; # code retour final = 2*256 = 512
        }
        else
        {
                #can not find table
                $session->close;
                exit 4; # code retour final = 4*256 = 1024
        }
}

sub requeteTable {
    my ($this, $oid) = @_;
    my $result = $session->get_table( -baseoid => $oid );
    request_error() if (!defined($result));
    my %tab = ();

    foreach my $i (Net::SNMP::oid_lex_sort(keys %{$result})) {
        my $index = $i;
        $index =~ s/$oid.//;
        $tab{ $index } = $result->{$i};  
        #print $index."--".$result->{$i}."\n";
    }

    return %tab;
}

sub requete {
    my ($this, $oid) = @_;
    my $result = $session->get_request($oid);
    request_error() if (!defined($result));
    return $result->{$oid}; 
}

sub deconnexion {
    my ($this) = @_;
    $session->close();
}

1;

Модуль AddresseMac.pm бесполезен, он просто конвертирует dec в hex и наоборот.

Спасибо за помощь,
большое вознаграждение тому, кто найдет оптимизацию ;)

PS: забыл сказать, я работаю на коммутаторе cisco 2960.


person eouti    schedule 10.05.2011    source источник


Ответы (1)


Вам может не понравиться этот ответ, но это одна из причин, по которой Net-SNMP поддерживает модуль perl (называемый просто SNMP), написанный с использованием C-bindings, а не с реализацией модуля all-in-perl, выполненной в < em>Net::SNMP заключается в том, что привязки C значительно быстрее. Джованни Марзо, написавший первоначальную реализацию привязки Net-SNMP C-binding, измерил, что реализация привязки C/perl работает в 10 раз быстрее, чем полностью написанная на Perl версия. А если вы начнете использовать аутентифицированный/зашифрованный SNMPv3, то он станет еще быстрее. Однако я не знаю, является ли это источником ваших проблем. Просто точка данных. Perl-профилировщик действительно даст вам знать.

Еще один момент, который следует учитывать: если вы запрашиваете множество хостов, подумайте об архитектуре своего кода, чтобы вы могли отправлять несколько запросов одновременно, используя асинхронные запросы и используя запросы GetBulk с использованием SNMPv2c. Эти две оптимизации также значительно увеличат скорость.

Обновлено со ссылками по запросу:

Обратите внимание, что в Net-SNMP есть функция gettable(), которая может вас заинтересовать, которая выполняет множество оптимизаций.

person Wes Hardaker    schedule 10.05.2011
comment
Хорошо, спасибо за ваш пост, я использую только SNMP v1, они не хотят использовать более новую версию SNMP. У вас есть ссылки на использование более быстрой библиотеки SNMP с Perl? - person eouti; 10.05.2011