#!/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 prepends to @INC, so put lower priority first # This is before run-time, so cannot condition this with normal 'if' logic. use lib '/home/lanforge/scripts'; use lib "./"; 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); #