📜 ⬆️ ⬇️

Load testing script to verify compliance with the current parameters of communication channels declared

Problematics


In organizations that use VPN / Internet data transmission channels leased from telecom operators to build a corporate branch network, a situation may arise sooner or later when it suddenly turns out that channels up to some remote divisions do not meet the stated requirements for bandwidth or if the channels are lightly loaded there are losses affecting the quality of the network services.

At the same time, the monitoring system regularly monitors the availability of channels, losses on them, delay, but due to the fact that communication channels are not always loaded with traffic, especially backup channels, it cannot timely detect all deviations of their parameters from those agreed with telecom operators. For such purposes, periodic load testing of channels is required, as a result of which a loss is checked on a channel while it is simultaneously loaded with traffic, utilizing the channel to almost the maximum bandwidth, and then controlling the amount of received traffic by the remote router. I want to share my insights regarding the automation of this process.

Idea


The idea is to develop and periodically use a script, which in my case runs on a Linux server in a central point of the corporate network. It is understood that the script interacts with routers, on which:


The script as a parameter receives a file with the ip addresses of the channel-forming interfaces of the routers, the communication channels of which we need to check. The number of lines in a file is not limited to any particular value. After sequential reading of the corresponding ip address from the input file, the script:
')
  1. It starts the background process of generating traffic in the direction of the read ip address, utilizing the bandwidth of the channel under test by less than 100% so as not to completely load the channel. I limited the process of generating traffic to 2 minutes with a band of generated traffic of 85% of the maximum channel bandwidth.
  2. A few seconds after the start of traffic generation, the SNMP protocol requests the current value of the received bytes for the router interface having the specified ip address. Remembers the current time t0.
  3. Launches a series of 60 ICMP packets. Fixes the number of losses.
  4. Re-queries the protocol SNMP value received bytes of the same interface. Remembers the current time t1. Calculates the duration of the measurement: t1-t0.
  5. Calculates the number of bytes received by the interface as a result of the measurement, based on which it determines the bandwidth of the interface by incoming traffic at the time of testing. I want to note that not all traffic generated by the script can be delivered to the router. This is possible in case of discrepancy of the channel bandwidth stated. Such facts are the script and should reveal.
  6. Displays as a result of the value:

    • the ratio of the band of delivered traffic to the band of generated traffic,
    • the number of losses on the channel and
    • the result of matching the channel parameters with the declared one, which is formed on the basis of the previous two values.


Features of the implementation


Traffic generator


The TCPBLAST / UDPBLAST program is used as a traffic generator, the source codes of which are available at the link: TCPBLAST / UDPBLAST

This program could not be better suited to the role of a traffic generator for this task, since In addition to the possibility of specifying the bandwidth of the generated traffic as command line parameters, it has the functionality to start generating traffic with a time limit, thus avoiding the risk of loading communication channels for a long time in case of unforeseen situations, for example, when the script crashes.

Detection of SNMP indexes of the interfaces of routers for the channels under test


The script receives at the entrance only the ip address of the router interface. This is quite enough to determine the SNMP interface index.

Used for these purposes OID: .1.3.6.1.2.1.4.20.1.2

Bandwidth detection of tested channels


Bandwidth of the interface and other parameters are determined using the corresponding OIDs with
substitution of SNMP interface index for them.

Script code


#!/usr/bin/perl use strict; use warnings; use POSIX qw(strftime); use SNMP; die "Usage: $0 <inputFile>" if ($#ARGV < 0); my $community = 'community'; ### pingLoss function ### sub pingLoss { my ($param) = @_; my @result = `ping $param`; foreach my $str (@result) { if ($str =~ /(\d+)%/ && $1 < 100) { return $1; } } return 100; } ### stressTest function for one host ### sub stressTest { my $ifIp = shift; my $ifIndex; my $ifName = ""; my $ifAlias = ""; my $ifSpeed = ""; my $ifInOctets; my $ifInOctetsBegin; my $ifInOctetsEnd; my $sendBytes; my $sysName = ""; my $testPeriod; my $pingLossCount = ""; my $inOutPercent = ""; my $testStatus; my $nowDateTime = strftime "%Y.%m.%d %H:%M:%S", localtime; if (&pingLoss("$ifIp -c 3 -i 0.2 -W 2") > 90) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tping error\n"; return 0; } my $sess = new SNMP::Session(DestHost => "$ifIp:161", Community => $community, Version => "2c", NonIncreasing => 1, UseLongNames => 1,); $sysName = $sess->get('.1.3.6.1.2.1.1.5.0'); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tsnmp sysName error\n"; return 0; } if ($sysName =~ m/^([\w_-]+)\./) { $sysName = $1; } $ifIndex = $sess->get('.1.3.6.1.2.1.4.20.1.2.' . $ifIp); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tsnmp ifIndex error\n"; return 0; } $ifName = $sess->get('.1.3.6.1.2.1.31.1.1.1.1.' . $ifIndex); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t$sysName\t\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tsnmp ifName error\n"; return 0; } $ifAlias = $sess->get('.1.3.6.1.2.1.31.1.1.1.18.' . $ifIndex); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t\t$ifSpeed\t$inOutPercent\t$pingLossCount\tsnmp ifAlias error\n"; return 0; } $ifSpeed = $sess->get('.1.3.6.1.2.1.2.2.1.5.' . $ifIndex); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t\t$inOutPercent\t$pingLossCount\tsnmp ifSpeed error\n"; return 0; } if ($ifSpeed > 30000000) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tHigh speed channel\n"; return 0; } my $sendBytesSec = $ifSpeed * 0.85 / 8; system("killall udpblast > /dev/null 2> /dev/null"); system("udpblast -c 1000000 --rate $sendBytesSec,100s $ifIp > /dev/null 2> /dev/null &"); sleep(5); my $testBegin = strftime "%s", localtime; $ifInOctetsBegin = $sess->get('.1.3.6.1.2.1.2.2.1.10.' . $ifIndex); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tsnmp ifInOctetsBegin error\n"; return 0; } $pingLossCount = &pingLoss("$ifIp -c 60 -W 1.5"); system("killall udpblast > /dev/null 2> /dev/null"); $ifInOctetsEnd = $sess->get('.1.3.6.1.2.1.2.2.1.10.' . $ifIndex); if ($sess->{ErrorNum}) { print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\tsnmp ifInOctetsEnd error\n"; return 0; } my $testEnd = strftime "%s", localtime; $testPeriod = $testEnd - $testBegin; $ifInOctets = $ifInOctetsEnd - $ifInOctetsBegin; $sendBytes = $sendBytesSec * $testPeriod * 1.04; $inOutPercent = sprintf("%d", $ifInOctets * 100 / $sendBytes); $testStatus = $inOutPercent > 89 && $pingLossCount < 6 ? "Good" : "Bad"; print "$nowDateTime\t$ifIp\t$sysName\t$ifName\t$ifAlias\t$ifSpeed\t$inOutPercent\t$pingLossCount\t$testStatus\n"; } ### Main ### open (inputFile, $ARGV[0]) or die "Failed to open $ARGV[0]: $!\n"; print "date time \tipAddress \tsysName \tifName \tifAlias \tifSpeed \tinOutPercent\tpingLossCount\ttestStatus\n"; foreach my $readString (<inputFile>) { my $ipAddress = ""; if ($readString =~ m/([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/ && $1 < 256 && $2 < 256 && $3 < 256 && $4 < 256) { $ipAddress = "$1.$2.$3.$4"; } else {next}; &stressTest($ipAddress); } 

I would be very happy constructive criticism in order to optimize the script algorithm.

Source: https://habr.com/ru/post/352592/


All Articles