mirror of
				https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
				synced 2025-10-30 18:27:53 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			405 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/perl -w
 | |
| #
 | |
| # Use this script to generate a batch of Generic lfping endpoints
 | |
| #
 | |
| # Examples:
 | |
| # ./lf_generic_ping.pl --mgr 192.168.1.100 --resource 1 --dest 10.1.1.1 -i wlan0 -i sta1 -i eth1
 | |
| # You should be able to place 1000 interfaces in the list
 | |
| #
 | |
| # Or all interfaces on a radio
 | |
| # ./lf_generic_ping.pl --mgr $mgr --resource 1 --dest 10.1.1.1 --radio wiphy0
 | |
| #
 | |
| # Or all macvlan on an ethernet port
 | |
| # ./lf_generic_ping.pl --mgr $mgr --resource 1 --dest 10.1.1.1 --parent eth1
 | |
| #
 | |
| # Or all interfaces matching a prefix:
 | |
| # ./lf_generic_ping.pl -m $mgr -r 1 -d 10.1.1.1 --match sta3
 | |
| #
 | |
| # The default name will be lfping_$endp name, use the --name
 | |
| # switch to alter the generic endpoint name, This allows multiple
 | |
| # generic connections to be created per port:
 | |
| #
 | |
| # for n in one two three four five six seven eight nine ten; do
 | |
| #  ./lf_generic_ping.pl -m 192.168.1.100 -r 1 -d 10.1.1.1 --match sta -name $n
 | |
| # done
 | |
| package main;
 | |
| use strict;
 | |
| use warnings;
 | |
| use diagnostics;
 | |
| use Carp;
 | |
| use Data::Dumper;
 | |
| $SIG{ __DIE__ }   = sub { Carp::confess( @_ )};
 | |
| $SIG{ __WARN__ }  = sub { Carp::confess( @_ )};
 | |
| use Getopt::Long;
 | |
| use Cwd qw(getcwd);
 | |
| my $cwd = getcwd();
 | |
| use lib '/home/lanforge/scripts';
 | |
| use List::Util qw(first);
 | |
| use LANforge::Endpoint;
 | |
| use LANforge::Port;
 | |
| use LANforge::Utils qw(fmt_cmd);
 | |
| use Net::Telnet ();
 | |
| 
 | |
| our $dest_ip_addr = "0.0.0.0";
 | |
| our $log_cli = "unset"; # use ENV{'LOG_CLI'}
 | |
| 
 | |
| our $usage = qq(Usage:
 | |
| $0 --mgr {host-name | IP}
 | |
|    --mgr_port {ip port}
 | |
|    --resource {resource}
 | |
|    --dest {destination IP}
 | |
|    --interface|-intf|-int|-i {source interface}
 | |
|     # You should be able to place 1000 interfaces in the list
 | |
|    --radio {wiphy} | --parent {eth}
 | |
|    --match {simple prefix, no stars or questions marks}
 | |
|    --cmd {"double quoted command"} # can contain special parameters
 | |
|    --name {prefix to name connection, appended with padded number}
 | |
| 
 | |
|  Examples:
 | |
|   $0 --mgr localhost --resource 1 --dest 192.168.0.1 -i wlan0 -i sta3000 --name "wlan_ping"
 | |
|   This will match just sta3000
 | |
| 
 | |
|  All interfaces on a parent radio or MAC VLANs on parent Ethernet port:
 | |
|   $0 --mgr localhost --resource 1 --dest 192.168.0.1 --radio wiphy0
 | |
|   This will match all stations whos parent is wiphy0: sta3 wlan0
 | |
| 
 | |
|   $0 --mgr localhost --resource 1 --dest 192.168.0.1 --parent eth1
 | |
|   This will match all MAC VLANs with parent eth1: eth1#0 eth1#1 eth1#2
 | |
| 
 | |
|  All interfaces matching a prefix:
 | |
|   $0 -m localhost -r 1 -d 192.168.0.1 --match sta3
 | |
|   This will match sta3 sta30 sta31 sta3000
 | |
| 
 | |
|  Please don't put single quotes in a command. A command can have these expansions:
 | |
|    %d destination ip or hostname
 | |
|    %i port IPv4 address
 | |
|    %p port name
 | |
| 
 | |
|  Example with curl wrapper provides better feedback to LANforge:
 | |
|  $0 --mgr cholla-f19 -r 2 -n curl_ex_ --match 'eth2#' \\
 | |
|    --cmd './scripts/lf_curl.sh -n 10 -o /tmp/curl_%p.out -i %i -p %p -d %d' --dest http://localhost/
 | |
| 
 | |
|  Example curl command doesn't provide good feedback to LANforge:
 | |
|  $0 --cmd "curl -sqL --dns-ipv4-addr %i --dns-interface %p \\
 | |
|    --interface %p --localaddr %i -o /tmp/results-%p http://%d/"
 | |
| 
 | |
|  The default name of the generic endpoints given will be "lfping_[port]".
 | |
|  You can create multiple generic connections per port by altering
 | |
|  the endpoint name with the --name switch.
 | |
|  Example of creating multiple connections per port in a loop:
 | |
|   for n in one two three four five six seven eight nine ten; do
 | |
|    $0 -m localhost -r 1 -d 10.1.1.1 --match sta -name \$n
 | |
|   done
 | |
| 
 | |
|  Example iperf3 server on eth1, 10.1.1.2:
 | |
|       iperf3 --forceflush --format k --precision 4 -s \\
 | |
|         --bind_dev %p -i 1 --pidfile /tmp/lf_helper_iperf_%p.pid
 | |
| 
 | |
|  Example iperf3 client on sta0 as 10.1.1.3:
 | |
|       iperf3 --forceflush --format k --precision 4 -c %d -t 60 --tos 0 -b 1K \\
 | |
|         --bind_dev %p -i 1 --pidfile /tmp/lf_helper_iperf_%p.pid
 | |
| 
 | |
|  If only a few of your generic commands start, check journalctl for
 | |
|  errors containing: 'cgroup: fork rejected by pids controller'
 | |
|  You want to set DefaultTasksMax=65535 in /etc/systemd/system.conf
 | |
|  then do a systemctl daemon-reload.
 | |
|  https://www.novell.com/support/kb/doc.php?id=7018594
 | |
| );
 | |
| 
 | |
| our $shelf_num    = 1;
 | |
| our $report_timer = 1000;
 | |
| our $test_mgr     = "default_tm";
 | |
| our $resource     = 1;
 | |
| our $lfmgr_host   = "localhost";
 | |
| our $lfmgr_port   = 4001;
 | |
| our $quiet        = "yes";
 | |
| our $quiesce      = 3;
 | |
| our $clear_on_start = 0;
 | |
| our $dest_ip;
 | |
| our @interfaces   = ();
 | |
| our $radio        = '';
 | |
| our $pattern      = '';
 | |
| our $name_pref    = "lfping";
 | |
| our $ref_cmd      = ''; # user supplied command
 | |
| our $ref_name     = '';
 | |
| our $verbose      = ((defined $ENV{'DEBUG'}) && ($ENV{'DEBUG'} eq "1")) ? 1:0;
 | |
| my $help;
 | |
| 
 | |
| if (@ARGV < 2) {
 | |
|    print $usage;
 | |
|    exit 0;
 | |
| }
 | |
| GetOptions
 | |
| (
 | |
|   'mgr|m=s'                   => \$::lfmgr_host,
 | |
|   'mgr_port|p=i'              => \$lfmgr_port,
 | |
|   'resource|r=i'              => \$::resource,
 | |
|   'quiet|q'                   => \$::quiet,
 | |
|   'verbose|v'                 => \$::verbose,
 | |
|   'radio|parent|o=s'          => \$::radio,
 | |
|   'match=s'                   => \$::pattern,
 | |
|   'interface|intf|int|i=s'    => \@::interfaces,
 | |
|   'dest_ip|dest|d=s'          => \$::dest_ip,
 | |
|   'name_pref|name|n=s'        => \$::name_pref,
 | |
|   'cmd|c=s'                   => \$::ref_cmd,
 | |
|   'help|h|?'                  => \$help,
 | |
| ) || (print($usage), exit(1));
 | |
| 
 | |
| #print "radio: $::radio, match: $::pattern, $::quiet, $::resource, $::dest_ip\n";
 | |
| 
 | |
| if ($help) {
 | |
|    print($usage) && exit(0);
 | |
| }
 | |
| if ($::quiet eq "0") {
 | |
|    $::quiet = "no";
 | |
| }
 | |
| elsif ($::quiet eq "1") {
 | |
|    $::quiet = "yes";
 | |
| }
 | |
| 
 | |
| # Open connection to the LANforge server.
 | |
| if (defined $log_cli) {
 | |
|   if ($log_cli ne "unset") {
 | |
|     # here is how we reset the variable if it was used as a flag
 | |
|     if ($log_cli eq "") {
 | |
|       $ENV{'LOG_CLI'} = 1;
 | |
|     }
 | |
|     else {
 | |
|       $ENV{'LOG_CLI'} = $log_cli;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| our $t = new Net::Telnet(Prompt => '/default\@btbits\>\>/',
 | |
|           Timeout => 20);
 | |
| $t->open(Host    => $lfmgr_host,
 | |
|          Port    => $lfmgr_port,
 | |
|          Timeout => 10);
 | |
| $t->waitfor("/btbits\>\>/");
 | |
| 
 | |
| # Configure our utils.
 | |
| our $utils = new LANforge::Utils();
 | |
| $utils->telnet($t);         # Set our telnet object.
 | |
| 
 | |
| if ($utils->isQuiet()) {
 | |
|   if (defined $ENV{'LOG_CLI'} && $ENV{'LOG_CLI'} ne "") {
 | |
|     $utils->cli_send_silent(0);
 | |
|   }
 | |
|   else {
 | |
|     $utils->cli_send_silent(1); # Do not show input to telnet
 | |
|   }
 | |
|   $utils->cli_rcv_silent(1);  # Repress output from telnet
 | |
| }
 | |
| else {
 | |
|   $utils->cli_send_silent(0); # Show input to telnet
 | |
|   $utils->cli_rcv_silent(0);  # Show output from telnet
 | |
| }
 | |
| $utils->log_cli("# $0 ".`date "+%Y-%m-%d %H:%M:%S"`);
 | |
| 
 | |
| our @ports_lines = split("\n", $::utils->doAsyncCmd("nc_show_ports 1 $::resource ALL"));
 | |
| chomp(@ports_lines);
 | |
| our %eid_map = ();
 | |
| my ($eid, $card, $port, $type, $mac, $dev, $parent, $ip);
 | |
| foreach my $line (@ports_lines) {
 | |
|   # collect all stations on that radio add them to @interfaces
 | |
|   if ($line =~ /^Shelf: /) {
 | |
|     $card = undef; $port = undef;
 | |
|     $type = undef; $parent = undef;
 | |
|     $eid = undef; $mac = undef;
 | |
|     $dev = undef;
 | |
|     $ip = undef;
 | |
|   }
 | |
| 
 | |
|   # careful about that comma after card!
 | |
|   # NO EID for Shelf: 1, Card: 1, Port: 2  Type: WIFI-Radio  Alias:
 | |
|   ($card, $port, $type) = $line =~ m/^Shelf: 1, Card: (\d+),\s+Port: (\d+)\s+Type: (\w+)/;
 | |
|   if ((defined $card) && ($card ne "") && (defined $port) && ($port ne "")) {
 | |
|     $eid = "1.${card}.${port}";
 | |
|     my $rh_eid = {
 | |
|       eid => $eid,
 | |
|       type => $type,
 | |
|       parent => undef,
 | |
|       dev => undef,
 | |
|     };
 | |
|     $::eid_map{$eid} = $rh_eid;
 | |
|     #print "\nfound eid $eid\n";
 | |
|   }
 | |
|   #elsif ($line =~ /^Shelf/) {
 | |
|   #  #print "NO EID for $line\n";
 | |
|   #}
 | |
| 
 | |
|   if (!(defined $eid) || ($eid eq "")) {
 | |
|     #print "NO EID for $line\n";
 | |
|     next;
 | |
|   }
 | |
|   ($mac, $dev) = $line =~ / MAC: ([0-9:a-fA-F]+)\s+DEV: (\S+)/;
 | |
|   if ((defined $mac) && ($mac ne "")) {
 | |
|    #print "$eid MAC: $line\n";
 | |
|     $::eid_map{$eid}->{mac} = $mac;
 | |
|     $::eid_map{$eid}->{dev} = $dev;
 | |
|   }
 | |
| 
 | |
|   ($parent) = $line =~ / Parent.Peer: (\S+) /;
 | |
|   if ((defined $parent) && ($parent ne "")) {
 | |
|     #print "$eid PARENT: $line\n";
 | |
|     $::eid_map{$eid}->{parent} = $parent;
 | |
|   }
 | |
| 
 | |
|   ($ip) = $line =~ m/ IP: *([^ ]+) */;
 | |
|   if ((defined $ip) && ($ip ne "")) {
 | |
|     #print "$eid IP: $line\n";
 | |
|     $::eid_map{$eid}->{ip} = $ip;
 | |
|   }
 | |
| } # foreach
 | |
| 
 | |
| #foreach $eid (keys %eid_map) {
 | |
| #  print "eid $eid ";
 | |
| #}
 | |
| 
 | |
| 
 | |
| if (defined $::radio) {
 | |
|   while (my ($eid, $rh_eid) = each %::eid_map) {
 | |
|     if ((defined $rh_eid->{parent}) && ($rh_eid->{parent} eq $::radio)) {
 | |
|       push(@interfaces, $rh_eid->{dev});
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| if (defined $::pattern && $pattern ne "") {
 | |
|    my $pat = $::pattern;
 | |
|    $pat =~ s/[+]//g;
 | |
|    # collect all stations on that resource add them to @interfaces
 | |
|    while (my($eid, $rh_eid) = each %::eid_map) {
 | |
|      if ((defined $rh_eid->{dev}) && ($rh_eid->{dev} =~ /$pat/)) {
 | |
|        push(@interfaces, $rh_eid->{dev});
 | |
|      }
 | |
|    }
 | |
| }
 | |
| 
 | |
| if (@interfaces < 1) {
 | |
|    print STDERR "One or more interfaces required.\n";
 | |
|    print $usage;
 | |
|    exit(1);
 | |
| }
 | |
| 
 | |
| print "Creating generic lfping endpoints using these interfaces: \n";
 | |
| print " ".join(", ", @interfaces)."\n";
 | |
| 
 | |
| =pod
 | |
| Example of generic created by GUI:
 | |
|    add_gen_endp test-1 1 3 sta3000 gen_generic
 | |
|    set_gen_cmd test-1 lfping  -p deadbeef -I sta3000 10.41.1.2
 | |
|    set_endp_quiesce test-1 3
 | |
|    set_endp_report_timer test-1 1000
 | |
|    set_endp_flag test-1 ClearPortOnStart 0
 | |
|    add_gen_endp D_test-1 1 3 sta3000 gen_generic
 | |
|    set_endp_flag D_test-1 unmanaged 1
 | |
|    set_endp_quiesce D_test-1 3
 | |
|    set_endp_report_timer D_test-1 1000
 | |
|    set_endp_flag D_test-1 ClearPortOnStart 0
 | |
| 
 | |
| Parameters that can be replaced:
 | |
|    %d destination ip or hostname
 | |
|    %i port IPv4 address
 | |
|    %p port name
 | |
| 
 | |
|    curl -sqL --dns-ipv4-addr %i --dns-interface %p --interface %p --localaddr %i -o /dev/null http://%d/
 | |
| 
 | |
| =cut
 | |
| sub create_generic {
 | |
|    my ($name, $port_name, $eid)=@_;
 | |
|    #print "= 1 =====================================================\n";
 | |
|    #print Dumper($eid);
 | |
|    my $endp_name = "${name_pref}_${port_name}";
 | |
|    my $type = "gen_generic";
 | |
|    my $rh_idr = $::eid_map{$eid};
 | |
|    my $port_ip = $rh_idr->{'ip'};
 | |
|    #print Dumper($rh_idr);
 | |
|    #print Dumper($rh_idr->{'ip'});
 | |
|    #print "$endp_name PORT_IP $port_ip \n";
 | |
|    #print "= 2 =====================================================\n";
 | |
|    my $ping_cmd = "lfping -I $port_name $::dest_ip";
 | |
|    if ((defined $::ref_cmd) && ($::ref_cmd ne "")) {
 | |
|       $ping_cmd = $::ref_cmd;
 | |
|       my $d_ip = '';
 | |
|       $d_ip = $::dest_ip if (defined $::dest_ip && $::dest_ip ne "");
 | |
|       $ping_cmd =~ s/%d/$::dest_ip/g if ($ping_cmd =~ /%d/);
 | |
| 
 | |
|       if (defined $port_name && $port_name ne "") {
 | |
|          $ping_cmd =~ s/%p/$port_name/g if ($ping_cmd =~ /%p/);
 | |
|       }
 | |
|       else {
 | |
|          print "no name for port $port_name\n";
 | |
|          return;
 | |
|       }
 | |
| 
 | |
|       if (defined $port_ip && $port_ip ne "") {
 | |
|          $ping_cmd =~ s/%i/$port_ip/g if ($ping_cmd =~ /%i/);
 | |
|       }
 | |
|       else {
 | |
|          print "no ip for port $port_name\n";
 | |
|          return;
 | |
|       }
 | |
|    }
 | |
|    $::command_map{$eid} = $ping_cmd;
 | |
| 
 | |
|    print "CMD: $ping_cmd\n" if ($::verbose);
 | |
| 
 | |
|    $::utils->doCmd($::utils->fmt_cmd("add_gen_endp", $endp_name, 1, $::resource, $port_name, $type));
 | |
|    $::utils->doCmd("set_gen_cmd $endp_name $ping_cmd");
 | |
|    $::utils->doCmd("set_endp_quiesce $endp_name $::quiesce");
 | |
|    $::utils->doCmd("set_endp_flag $endp_name ClearPortOnStart $::clear_on_start");
 | |
|    $::utils->doCmd("set_endp_report_timer $endp_name $::report_timer");
 | |
| 
 | |
|    # we also need to add the opposite unmanaged endpoint
 | |
|    $::utils->doCmd("add_gen_endp D_$endp_name 1 $::resource $port_name gen_generic");
 | |
|    $::utils->doCmd("set_endp_flag D_$endp_name unmanaged 1");
 | |
|    $::utils->doCmd("set_endp_quiesce D_$endp_name $::quiesce");
 | |
|    $::utils->doCmd("set_endp_flag D_$endp_name ClearPortOnStart $::clear_on_start");
 | |
|    $::utils->doCmd("set_endp_report_timer D_$endp_name $::report_timer");
 | |
| 
 | |
|    # tie the knot with a CX
 | |
|    $::utils->doCmd("add_cx CX_$endp_name default_tm $endp_name D_$endp_name");
 | |
|    $::utils->doCmd("set_cx_report_timer default_tm CX_$endp_name $::report_timer cxonly");
 | |
| }
 | |
| 
 | |
| #print Dumper(\@interfaces);
 | |
| #print Dumper(\%::eid_map);
 | |
| our %command_map = ();
 | |
| my @map_keys = sort keys %eid_map;
 | |
| for my $port (sort @interfaces) {
 | |
|    my $endp_name = "${name_pref}_$port";
 | |
|    my $matching_eid = "";
 | |
|    #print "Searching for port $port ";
 | |
|    #while (my ($eid, $rh_pid) = each %eid_map) {
 | |
|    for my $eid (@map_keys) {
 | |
|       my $rh_pid = $eid_map{$eid};
 | |
|       #print " $port/$rh_pid->{dev} ";
 | |
|       if ("$port" eq "$rh_pid->{dev}") {
 | |
|          #print " ** ";
 | |
|          $matching_eid = $eid;
 | |
|          last;
 | |
|       }
 | |
|    }
 | |
|    if ($matching_eid eq "") {
 | |
|       print "\nSkipping $port no eid [$matching_eid]\n";
 | |
|       next;
 | |
|    }
 | |
|    #print "\n= 3 =====================================================\n";
 | |
|    #print " $matching_eid => ".$eid_map{$matching_eid}->{dev}."\n";
 | |
|    #print Dumper($eid_map{$matching_eid});
 | |
|    #print "= 4 =====================================================\n";
 | |
| 
 | |
| 
 | |
|    if (! (defined $eid_map{$matching_eid}->{ip})
 | |
|       || $eid_map{$matching_eid}->{ip} eq ""
 | |
|       || $eid_map{$matching_eid}->{ip} eq "0.0.0.0") {
 | |
|       print "\nSkipping $port: ".$eid_map{$matching_eid}->{ip}."\n";
 | |
|       sleep 1;
 | |
|       next;
 | |
|    }
 | |
|    create_generic($endp_name, $port, $matching_eid);
 | |
| }
 | |
| #print Dumper(\%command_map);
 | |
| 
 | |
| #
 | 
