I have written a Perl script (attached) which interrogates my Draytek Vigor 130 every minute and dumps information from vdsl stats and vdsl stats more into a .csv file.
It runs on FreeBSD, invoked every minute via a cron job as vigor_stats.pl --auto. This writes a header line if the daily .csv file does not yet exist, and always writes a data line.
It will probably run on any UNIX-like system.
It might run on Cygwin.
It might even run with Strawberry Perl on Windows.
You will probably need to install some CPAN modules - trying to run the script or compile it with perl -wc will fail when a used module is missing.
The first bit of Perl I have written in 4 years!
Darn, can't attach a .pl file, so here it is inline.
#!/usr/bin/env perl
use Modern::Perl '2020';
use autodie;
use Getopt::Long qw(HelpMessage);
use DateTime ();
use Net::Telnet ();
GetOptions(
"debug" => \( my $debug = 0 ),
'modem=s' => \( my $modem = '192.168.0.1' ),
'user=s' => \( my $user = 'admin' ),
'passwd=s' => \( my $passwd = 'admin' ),
"auto" => \( my $auto = 0 ),
"prefix=s" => \( my $prefix = '~/vigor_stats/' ),
"fields" => \( my $fields = 0 ),
"header" => \( my $header = 0 ),
"data" => \( my $data = 0 ),
'help' => sub { HelpMessage(0) },
) or HelpMessage(0);
HelpMessage(1) unless ( $modem && $user && $passwd );
HelpMessage(1) unless ( $auto || $fields || $data || $header );
HelpMessage(1) if ( $fields && ( $data || $header || $auto ) );
HelpMessage(1) if ( $auto && !$prefix );
my @wanted = ( # fields output in this order
'Running Mode',
'State',
'DS Actual Rate',
'US Actual Rate',
'DS Attainable Rate',
'US Attainable Rate',
'DS Path Mode',
'US Path Mode',
'DS Interleave Depth',
'US Interleave Depth',
'NE Current Attenuation',
'Cur SNR Margin',
'NE CRC Count',
'FE CRC Count',
'NE ES Count',
'FE ES Count',
'Xdsl Reset Times',
'Xdsl Link Times',
'Far Current Attenuation',
'Far SNR Margin',
'NE_ReTxEnable',
'FE_ReTxEnable',
'NE_LOS',
'FE_LOS',
'NE_LOF',
'FE_LOF',
'NE_LPR',
'FE_LPR',
'NE_LOM',
'FE_LOM',
'NE_NCD',
'FE_NCD',
'NE_LCD',
'FE_LCD',
'NE_FECS',
'FE_FECS',
'NE_ES',
'FE_ES',
'NE_SES',
'FE_SES',
'NE_LOSS',
'FE_LOSS',
'NE_UAS',
'FE_UAS',
'NE_HECError',
'FE_HECError',
'NE_CRC',
'FE_CRC',
'NE_INP',
'FE_INP',
'NE_InterleaveDelay',
'FE_InterleaveDelay',
'DSLAM CHIPSET VENDOR',
'NE_Trellis',
'FE_Trellis',
'NE_Bitswap',
'FE_Bitswap',
'NE_20BitSupport',
'FE_20BitSupport',
'NE_LatencyPath',
'FE_LatencyPath',
'NE_VirtualNoise',
'FE_VirtualNoise',
'NE_SosSuccess',
'FE_SosSuccess',
'NE_RsCorrection',
'FE_RsCorrection',
'DS actual PSD',
'US actual PSD',
'NE_NFEC',
'FE_NFEC',
'NE_RFEC',
'FE_RFEC',
'NE_LSYMB',
'FE_LSYMB',
'NE_INTLVBLOCK',
'FE_INTLVBLOCK',
'NE_AELEM',
'CO ITU Version[0]',
'CO ITU Version[1]',
'ITU Version[0]',
'ITU Version[1]',
'VDSL Firmware Version',
'Power Management Mode',
'Test Mode',
# 'FE_AELEM',
);
my $dt = DateTime->now;
my $now = $dt->datetime . "Z"; # ISO GMT date
my $ymd = $dt->ymd; # date
my %data; # key field name value field content
my @fields; # field names found in input
my $t = new Net::Telnet(
Prompt => '/> /',
Timeout => 10,
);
$t->open($modem);
$t->waitfor('/account:/i');
$t->print($user);
$t->waitfor('/password:/i');
$t->print($passwd);
$t->waitfor('/> /');
foreach my $section ( 'vdsl status', 'vdsl status more' ) {
foreach my $line ( $t->cmd($section) ) {
$line =~ s/\s+/ /g; # compress white space
$line =~ s/ (bps|dB|\(.*\))//g; # simplify
$line =~ s/( \d+)\. +(\d+)/$1.$2/g; # remove spaces in numbers
$line =~ s/^ *(.*) $/$1/; # remove white space start and end
next unless $line =~ m/:/;
($debug) && say( STDERR "$section: '$line'" );
if ( $section eq "vdsl status" ) {
if ( $line =~ m/^(.*) : (\S*) *(.*) : (.*)$/ ) {
($debug) && say( STDERR "'$1' = '$2'" );
$data{$1} = $2;
($debug) && say( STDERR "'$3' = '$4'" );
$data{$3} = $4;
push( @fields, $1, $3 );
}
elsif ( $line =~ m/^(.*) : (.*)$/ ) {
($debug) && say( STDERR "'$1' = '$2'" );
$data{$1} = $2;
push( @fields, $1 );
}
next;
}
if ( $section eq "vdsl status more" ) {
if ( $line =~ m/^(.*) : (\S*) *(\S*)$/ ) {
($debug) && say( STDERR "'NE_$1' = '$2'" );
$data{"NE_$1"} = $2;
($debug) && say( STDERR "'FE_$1' = '$3'" );
$data{"FE_$1"} = $3;
push( @fields, "NE_$1", "FE_$1" );
}
}
}
}
$t->print('exit');
if ($debug) {
foreach my $item ( sort keys %data ) {
my $value = $data{$item};
say( STDERR"data '$item' = '$value'" );
}
}
if ($fields) {
foreach my $item (@fields) {
say("# '$item',");
}
exit(0);
}
if ($auto) {
my $file = glob("$prefix$ymd.csv"); # accepts ~ !
if ( -e $file ) {
}
else {
$header++;
}
$data++;
open( my $fh, '>>:encoding(UTF-8)', $file );
select($fh);
}
if ($header) {
say( join( ",", $now, @wanted ) );
}
if ($data) {
my @cells = ($now);
foreach my $item (@wanted) {
die("field $item not extracted") unless ( exists $data{$item} );
push( @cells, $data{$item} );
}
say( join( ",", @cells ) );
}
=head1 NAME
vigor_stats.pl - extract stats from a Vigor 130
$Header: /home/aw1/vigor/RCS/vigor_stats.pl,v 1.1 2020/02/26 01:37:00 aw1 Exp aw1 $
=head1 SYNOPSIS
--modem Modem IP or name
--user Modem username
--passwd Modem password
--auto Output to file with prefix and automatic filename
May not be used with --data --header or --fields
--prefix File prefix
--data Extract data as CSV to STDOUT
--header Extract header as CSV to STDOUT
--fields Extract names of all fields to STDOUT
may not be used with --data or --header
Normal use
vigor_stats.pl --auto
=cut