You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1107 lines
44 KiB
1107 lines
44 KiB
2 years ago
|
#!/usr/bin/perl
|
||
|
#
|
||
|
|
||
|
use strict;
|
||
|
use warnings;
|
||
|
use Anvil::Tools;
|
||
|
use POSIX qw/ceil/;;
|
||
|
use Data::Dumper;
|
||
|
use Time::HiRes qw(usleep);
|
||
|
|
||
|
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
|
||
|
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
|
||
|
if (($running_directory =~ /^\./) && ($ENV{PWD}))
|
||
|
{
|
||
|
$running_directory =~ s/^\./$ENV{PWD}/;
|
||
|
}
|
||
|
|
||
|
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
|
||
|
$| = 1;
|
||
|
|
||
|
my $anvil = Anvil::Tools->new();
|
||
|
|
||
|
$anvil->data->{switches}{'max-mtu'} = "";
|
||
|
$anvil->data->{switches}{password} = "";
|
||
|
$anvil->data->{switches}{stepping} = "";
|
||
|
$anvil->data->{switches}{source} = "";
|
||
|
$anvil->data->{switches}{target} = "";
|
||
|
$anvil->data->{switches}{tests} = "";
|
||
|
$anvil->data->{switches}{y} = "";
|
||
|
$anvil->Get->switches;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
'switches::max-mtu' => $anvil->data->{switches}{'max-mtu'},
|
||
|
'switches::password' => $anvil->Log->is_secure($anvil->data->{switches}{password}),
|
||
|
'switches::stepping' => $anvil->data->{switches}{stepping},
|
||
|
'switches::source' => $anvil->data->{switches}{source},
|
||
|
'switches::target' => $anvil->data->{switches}{target},
|
||
|
'switches::tests' => $anvil->data->{switches}{tests},
|
||
|
'switches::y' => $anvil->data->{switches}{y},
|
||
|
}});
|
||
|
|
||
|
checks($anvil);
|
||
|
print "Checks passed, beginning.\n";
|
||
|
|
||
|
if (not $anvil->data->{sys}{max_usable_mtu})
|
||
|
{
|
||
|
find_maximum_usable_mtu($anvil);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
'sys::highest_good_mtu' => $anvil->data->{sys}{highest_good_mtu},
|
||
|
}});
|
||
|
if ($anvil->data->{sys}{highest_good_mtu} < 1500)
|
||
|
{
|
||
|
print "[ Error ] - The detected maximum usable MTU size appears invalid!\n";
|
||
|
print "[ Error ] - You may need to restore the default MTU of one or both nodes using:\n";
|
||
|
my $remote_shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify \"".$anvil->data->{sys}{target_interface}{name}."\" 802-3-ethernet.mtu 1500 && ".$anvil->data->{path}{exe}{nmcli}." connection up \"".$anvil->data->{sys}{target_interface}{name}."\"";
|
||
|
my $local_shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify \"".$anvil->data->{sys}{source_interface}{name}."\" 802-3-ethernet.mtu 1500 && ".$anvil->data->{path}{exe}{nmcli}." connection up \"".$anvil->data->{sys}{source_interface}{name}."\"";
|
||
|
print " remote: ".$local_shell_call."\n";
|
||
|
print " local: ".$remote_shell_call."\n\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "\nMaximum MTU for this network statically set to: [".$anvil->data->{sys}{max_usable_mtu}."]\n";
|
||
|
}
|
||
|
|
||
|
do_tests($anvil);
|
||
|
|
||
|
print "All tests complete!\n\n";
|
||
|
print "Graphs:\n\n";
|
||
|
print_graphs($anvil);
|
||
|
|
||
|
|
||
|
$anvil->nice_exit({exit_code => 0});
|
||
|
|
||
|
#############################################################################################################
|
||
|
# Functions #
|
||
|
#############################################################################################################
|
||
|
|
||
|
sub print_graphs
|
||
|
{
|
||
|
my ($anvil) = @_;
|
||
|
|
||
|
$anvil->data->{sys}{highest_value} = 0;
|
||
|
$anvil->data->{sys}{peak_mtu} = 0;
|
||
|
foreach my $mtu (keys %{$anvil->data->{iperf}{mtu}})
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mtu => $mtu }});
|
||
|
foreach my $key (keys %{$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}})
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"iperf::mtu::${mtu}::test::avg::${key}" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{$key},
|
||
|
}});
|
||
|
if ($anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{$key} > $anvil->data->{sys}{highest_value})
|
||
|
{
|
||
|
$anvil->data->{sys}{highest_value} = $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{$key};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::highest_value" => $anvil->data->{sys}{highest_value},
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$anvil->data->{sys}{highest_value} = sprintf("%.1f", $anvil->data->{sys}{highest_value});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::highest_value" => $anvil->data->{sys}{highest_value},
|
||
|
}});
|
||
|
|
||
|
$anvil->data->{sys}{peak_mtu} = 0;
|
||
|
$anvil->data->{sys}{peak_average} = 0;
|
||
|
foreach my $mtu (sort {$a <=> $b} keys %{$anvil->data->{iperf}{mtu}})
|
||
|
{
|
||
|
if ($anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average} > $anvil->data->{sys}{peak_average})
|
||
|
{
|
||
|
$anvil->data->{sys}{peak_average} = $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average};
|
||
|
$anvil->data->{sys}{peak_mtu} = $mtu;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::peak_average" => $anvil->data->{sys}{peak_average},
|
||
|
"sys::peak_mtu" => $anvil->data->{sys}{peak_mtu},
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my $columns = get_tput_cols($anvil);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { columns => $columns }});
|
||
|
|
||
|
$anvil->data->{sys}{max_graph_width} = ($columns - 10);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::max_graph_width" => $anvil->data->{sys}{max_graph_width},
|
||
|
}});
|
||
|
foreach my $mtu (sort {$b cmp $a} keys %{$anvil->data->{iperf}{mtu}})
|
||
|
{
|
||
|
my $bar_minus = 7;
|
||
|
my $say_mtu = sprintf("%4s", $mtu);
|
||
|
if (length($anvil->data->{sys}{max_usable_mtu}) == 5)
|
||
|
{
|
||
|
$bar_minus = 8;
|
||
|
$say_mtu = sprintf("%5s", $mtu);
|
||
|
}
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
bar_minus => $bar_minus,
|
||
|
say_mtu => $say_mtu,
|
||
|
}});
|
||
|
print "[ ".$say_mtu." bytes ]";
|
||
|
for (0..($anvil->data->{sys}{max_graph_width} - $bar_minus))
|
||
|
{
|
||
|
print "-";
|
||
|
}
|
||
|
print "\n";
|
||
|
print " Tx: "; print_dots($anvil, $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth});
|
||
|
print " Rx: "; print_dots($anvil, $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth});
|
||
|
print "Avg: "; print_dots($anvil, $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average});
|
||
|
}
|
||
|
for (0..($anvil->data->{sys}{max_graph_width} + 8))
|
||
|
{
|
||
|
print "-";
|
||
|
}
|
||
|
print "\n";
|
||
|
print "The peak average bandwidth was: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{sys}{peak_average}})."], achieved with the MTU: [".$anvil->data->{sys}{peak_mtu}."]\n\n";
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
sub print_dots
|
||
|
{
|
||
|
my ($anvil, $bandwidth) = @_;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bandwidth => $bandwidth }});
|
||
|
|
||
|
my $say_bandwidth = $anvil->Convert->bytes_to_human_readable({'bytes' => $bandwidth});
|
||
|
my $overhead = 9;
|
||
|
my $less_dots = 4 + $overhead;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
say_bandwidth => $say_bandwidth,
|
||
|
'sys::highest_value' => $anvil->data->{sys}{highest_value},
|
||
|
}});
|
||
|
if (length($anvil->data->{sys}{highest_value}) == 7)
|
||
|
{
|
||
|
$say_bandwidth = sprintf("%7s", $say_bandwidth);
|
||
|
$less_dots = 6 + $overhead;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
say_bandwidth => $say_bandwidth,
|
||
|
less_dots => $less_dots,
|
||
|
}});
|
||
|
}
|
||
|
elsif (length($anvil->data->{sys}{highest_value}) == 6)
|
||
|
{
|
||
|
$say_bandwidth = sprintf("%6s", $say_bandwidth);
|
||
|
$less_dots = 6 + $overhead;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
say_bandwidth => $say_bandwidth,
|
||
|
less_dots => $less_dots,
|
||
|
}});
|
||
|
}
|
||
|
elsif (length($anvil->data->{sys}{highest_value}) == 5)
|
||
|
{
|
||
|
$say_bandwidth = sprintf("%5s", $say_bandwidth);
|
||
|
$less_dots = 5 + $overhead;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
say_bandwidth => $say_bandwidth,
|
||
|
less_dots => $less_dots,
|
||
|
}});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$say_bandwidth = sprintf("%4s", $say_bandwidth);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_bandwidth => $say_bandwidth }});
|
||
|
}
|
||
|
my $percent = sprintf("%.0f", (($bandwidth / $anvil->data->{sys}{highest_value}) * 100));
|
||
|
my $dots = $anvil->data->{sys}{max_graph_width} * ($percent / 100);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
percent => $percent,
|
||
|
dots => $dots,
|
||
|
}});
|
||
|
|
||
|
print " ".$say_bandwidth."/sec |";
|
||
|
|
||
|
my $full_dot = $dots;
|
||
|
my $remainder = 0;
|
||
|
if ($dots =~ /(\d+)\.(\d)/)
|
||
|
{
|
||
|
$full_dot = $1;
|
||
|
$remainder = $2;
|
||
|
}
|
||
|
|
||
|
if ($full_dot > $less_dots)
|
||
|
{
|
||
|
$full_dot -= $less_dots;
|
||
|
for (0..$full_dot)
|
||
|
{
|
||
|
print "#";
|
||
|
}
|
||
|
if (($remainder > 3) && ($remainder < 7))
|
||
|
{
|
||
|
print ">";
|
||
|
}
|
||
|
}
|
||
|
print "\n";
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
sub get_tput_cols
|
||
|
{
|
||
|
my ($anvil) = @_;
|
||
|
|
||
|
my $columns = 80;
|
||
|
|
||
|
# This can't be called through IO::Handle as it always returns 80.
|
||
|
my $shell_call = $anvil->data->{path}{exe}{tput}." cols";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
$columns = `$shell_call`;
|
||
|
chomp $columns;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { columns => $columns }});
|
||
|
|
||
|
return ($columns);
|
||
|
}
|
||
|
|
||
|
sub do_tests
|
||
|
{
|
||
|
my ($anvil) = @_;
|
||
|
print "\n-=] Beginning 'iperf' testing, averaging: [".$anvil->data->{sys}{iperf_test_count}."] tests.\n\n";
|
||
|
|
||
|
# Current MTU size to test.
|
||
|
my $iperf_mtu = 1500;
|
||
|
|
||
|
# Give the user an idea of how long they have to go make coffee.
|
||
|
my $skipped_iterations = (($anvil->data->{sys}{min_usable_mtu} / $anvil->data->{sys}{iperf_mtu_stepping}) - 1);
|
||
|
my $total_iterations = ceil($anvil->data->{sys}{highest_good_mtu} / $anvil->data->{sys}{iperf_mtu_stepping});
|
||
|
my $actual_iterations = ($total_iterations - $skipped_iterations);
|
||
|
my $per_test_time = ($anvil->data->{sys}{iperf_test_count} * 20); # 10sec per half/full duplex.
|
||
|
my $total_seconds = $actual_iterations * $per_test_time;
|
||
|
my $total_minutes = $total_seconds / 60;
|
||
|
$total_minutes = sprintf("%.1f", $total_minutes);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
's1:skipped_iterations' => $skipped_iterations,
|
||
|
's2:total_iterations' => $total_iterations,
|
||
|
's3:actual_iterations' => $actual_iterations,
|
||
|
's4:per_test_time' => $per_test_time,
|
||
|
's5:total_seconds' => $total_seconds,
|
||
|
's6:total_minutes' => $total_minutes,
|
||
|
}});
|
||
|
print "Please be patient. I expect this will take roughly: [".$total_minutes." minutes].\n";
|
||
|
print " - Time needed to change the MTU will add to this estimated time.\n";
|
||
|
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
iperf_mtu => $iperf_mtu,
|
||
|
"sys::highest_good_mtu" => $anvil->data->{sys}{highest_good_mtu},
|
||
|
}});
|
||
|
while ($iperf_mtu <= $anvil->data->{sys}{highest_good_mtu})
|
||
|
{
|
||
|
run_iperf_tests($anvil, $iperf_mtu);
|
||
|
|
||
|
# I want to always test the maximum MTU, which this will do.
|
||
|
if ($iperf_mtu != $anvil->data->{sys}{highest_good_mtu})
|
||
|
{
|
||
|
$iperf_mtu += $anvil->data->{sys}{iperf_mtu_stepping};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { iperf_mtu => $iperf_mtu }});
|
||
|
if ($iperf_mtu > $anvil->data->{sys}{highest_good_mtu})
|
||
|
{
|
||
|
$iperf_mtu = $anvil->data->{sys}{highest_good_mtu};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { iperf_mtu => $iperf_mtu }});
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Done.
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
sub run_iperf_tests
|
||
|
{
|
||
|
my ($anvil, $mtu) = @_;
|
||
|
|
||
|
print "\nTesting iperf at MTU: [".$mtu." Bytes];\n";
|
||
|
my ($new_remote_mtu) = alter_mtu($anvil, "remote", $mtu);
|
||
|
if ($mtu ne $new_remote_mtu)
|
||
|
{
|
||
|
print "[ ERROR ] I was unable change the MTU up to: [".$mtu."] on remote.\n";
|
||
|
print "[ ERROR ] Still at an MTU of: [".$mtu."].\n\n";
|
||
|
exit 5;
|
||
|
}
|
||
|
my ($new_local_mtu) = alter_mtu($anvil, "local", $mtu);
|
||
|
if ($mtu ne $new_local_mtu)
|
||
|
{
|
||
|
print "[ ERROR ] I was unable change the MTU up to: [".$mtu."] on local.\n";
|
||
|
print "[ ERROR ] Still at an MTU of: [".$mtu."].\n\n";
|
||
|
exit 6;
|
||
|
}
|
||
|
|
||
|
# Beginning Tests:
|
||
|
my $iperf_test_count = $anvil->data->{sys}{iperf_test_count};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { iperf_test_count => $iperf_test_count }});
|
||
|
foreach my $i (1..$iperf_test_count)
|
||
|
{
|
||
|
($anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_seconds},
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_transfer},
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_bandwidth},
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_seconds},
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_transfer},
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_bandwidth}) = call_iperf($anvil, $i);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"s1:iperf::mtu::${mtu}::test::${i}::tx_seconds" => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_seconds},
|
||
|
"s2:iperf::mtu::${mtu}::test::${i}::tx_transfer" => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_transfer},
|
||
|
"s3:iperf::mtu::${mtu}::test::${i}::tx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_bandwidth},
|
||
|
"s4:iperf::mtu::${mtu}::test::${i}::rx_seconds" => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_seconds},
|
||
|
"s5:iperf::mtu::${mtu}::test::${i}::rx_transfer" => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_transfer},
|
||
|
"s6:iperf::mtu::${mtu}::test::${i}::rx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_bandwidth},
|
||
|
}});
|
||
|
print " - [".$i."/".$iperf_test_count."]; Transmit: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_bandwidth}})."/sec], Receive: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_bandwidth}})."/sec]\n";
|
||
|
|
||
|
# Add to the totals for later averaging.
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth} += $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{tx_bandwidth};
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth} += $anvil->data->{iperf}{mtu}{$mtu}{test}{$i}{rx_bandwidth};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"s1:iperf::mtu::${mtu}::test::avg::tx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth},
|
||
|
"s2:iperf::mtu::${mtu}::test::avg::rx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth},
|
||
|
}});
|
||
|
}
|
||
|
print "Done!\n";
|
||
|
|
||
|
# Average sums.
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth} /= $iperf_test_count;
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth} /= $iperf_test_count;
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average} = ($anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth} + $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth}) / 2;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"s1:iperf::mtu::${mtu}::test::avg::tx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth},
|
||
|
"s2:iperf::mtu::${mtu}::test::avg::rx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth},
|
||
|
"s3:iperf::mtu::${mtu}::test::avg::total_average" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average},
|
||
|
}});
|
||
|
|
||
|
# Round to zero
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth} = $anvil->Convert->round({number => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth}});
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth} = $anvil->Convert->round({number => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth}});
|
||
|
$anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average} = $anvil->Convert->round({number => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average}});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"s1:iperf::mtu::${mtu}::test::avg::tx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth}." Bytes/Sec (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth}}).")",
|
||
|
"s2:iperf::mtu::${mtu}::test::avg::rx_bandwidth" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth}." Bytes/Sec (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth}}).")",
|
||
|
"s3:iperf::mtu::${mtu}::test::avg::total_average" => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average}." Bytes/Sec (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average}}).")",
|
||
|
}});
|
||
|
|
||
|
# # Report average
|
||
|
print " - Average TX: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{tx_bandwidth}})."/sec], RX: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{rx_bandwidth}})."/sec], Total Average: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{iperf}{mtu}{$mtu}{test}{avg}{total_average}})."/sec]\n";
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
sub call_iperf
|
||
|
{
|
||
|
my ($anvil, $test)=@_;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
test => $test,
|
||
|
}});
|
||
|
|
||
|
# Start the daemon on the peer.
|
||
|
my $shell_call = $anvil->data->{path}{exe}{iperf3}." --server --daemon --one-off";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
my ($output, $error, $return_code) = $anvil->Remote->call({
|
||
|
target => $anvil->data->{switches}{target},
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
shell_call => $shell_call,
|
||
|
});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
error => $error,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
|
||
|
# Sleep for a short time to make sure the server has started and is listening.
|
||
|
usleep($anvil->data->{sys}{iperf_micro_sleep});
|
||
|
|
||
|
# Now call iperf3 locally.
|
||
|
# Get the output in Kbps, running for 11 seconds, discarding the first 1 second to ignore TCP ramp up
|
||
|
$shell_call = $anvil->data->{path}{exe}{iperf3}." --client ".$anvil->data->{switches}{target}." --format K --interval 0 --time 11 --omit 1 ";
|
||
|
($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
foreach my $line (split/\n/, $output)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
|
||
|
}
|
||
|
|
||
|
=cut
|
||
|
Connecting to host 192.168.122.251, port 5201
|
||
|
[ 5] local 192.168.122.252 port 41998 connected to 192.168.122.251 port 5201
|
||
|
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||
|
[ 5] 0.00-11.00 sec 78.9 GBytes 7524333 KBytes/sec 0 3.01 MBytes
|
||
|
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||
|
[ ID] Interval Transfer Bitrate Retr
|
||
|
[ 5] 0.00-11.00 sec 78.9 GBytes 7524333 KBytes/sec 0 sender
|
||
|
[ 5] 0.00-11.04 sec 79.2 GBytes 7521387 KBytes/sec receiver
|
||
|
|
||
|
iperf Done.
|
||
|
=cut
|
||
|
|
||
|
my $tx_seconds = 0;
|
||
|
my $tx_transfer = 0;
|
||
|
my $tx_bandwidth = 0;
|
||
|
my $rx_seconds = 0;
|
||
|
my $rx_transfer = 0;
|
||
|
my $rx_bandwidth = 0;
|
||
|
foreach my $line (split/\n/, $output)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
|
||
|
if ($line =~ /0.00-(\d+.*?)\s+sec\s+(\d+.* \wBytes)\s+(\d+)\s+KBytes\/sec/)
|
||
|
{
|
||
|
my $seconds = $1;
|
||
|
my $transfer = $2;
|
||
|
my $bandwidth = $3;
|
||
|
if ($line =~ / sender$/)
|
||
|
{
|
||
|
$tx_seconds = $seconds - 1;
|
||
|
$tx_transfer = $transfer;
|
||
|
$tx_bandwidth = $bandwidth;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
tx_seconds => $tx_seconds,
|
||
|
tx_transfer => $tx_transfer,
|
||
|
tx_bandwidth => $tx_bandwidth,
|
||
|
}});
|
||
|
}
|
||
|
elsif ($line =~ / receiver$/)
|
||
|
{
|
||
|
$rx_seconds = $seconds - 1;
|
||
|
$rx_transfer = $transfer;
|
||
|
$rx_bandwidth = $bandwidth;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
rx_seconds => $tx_seconds,
|
||
|
rx_transfer => $tx_transfer,
|
||
|
rx_bandwidth => $tx_bandwidth,
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Convert from KBytes to Bytes
|
||
|
$tx_bandwidth = $anvil->Convert->human_readable_to_bytes({size => $tx_bandwidth, type => "KiB"});
|
||
|
$rx_bandwidth = $anvil->Convert->human_readable_to_bytes({size => $rx_bandwidth, type => "KiB"});
|
||
|
|
||
|
return ($tx_seconds, $tx_transfer, $tx_bandwidth, $rx_seconds, $rx_transfer, $rx_bandwidth);
|
||
|
}
|
||
|
|
||
|
sub find_maximum_usable_mtu
|
||
|
{
|
||
|
my ($anvil) = @_;
|
||
|
|
||
|
# Test the initial MTU of 1500.
|
||
|
print "Initial test with an MTU of '1500' to confirm testing method;\n";
|
||
|
|
||
|
# This is the increment, in bytes starting at 1500, that the MTU will be raised to the maximum.
|
||
|
$anvil->data->{sys}{iperf_mtu_stepping} = 500;
|
||
|
|
||
|
# This sets a maximum MTU to test for. It is useful if setting a too-high MTU will break the network
|
||
|
# link.
|
||
|
$anvil->data->{sys}{mtu_maximum} = 9000;
|
||
|
|
||
|
# If set to 0, the program will roughly detect the maximum MTU. If set, the value becomes the maximum
|
||
|
# MTU (and assumes you've already ensured that your interfaces and network infrastructure handle it).
|
||
|
$anvil->data->{sys}{max_usable_mtu} = 0;
|
||
|
if ($anvil->data->{switches}{'max-mtu'})
|
||
|
{
|
||
|
if (($anvil->data->{switches}{'max-mtu'} =~ /^\d+$/) && ($anvil->data->{switches}{'max-mtu'} > 1500))
|
||
|
{
|
||
|
$anvil->data->{sys}{max_usable_mtu} = $anvil->data->{switches}{'max-mtu'};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::max_usable_mtu" => $anvil->data->{sys}{max_usable_mtu},
|
||
|
}});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "[ Error ] - The '--max-mtu' was set to: [".$anvil->data->{switches}{'max-mtu'}."] which is invalid. It must be a whole number larger than 1500.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# This is the minimum MTU. It should never change from 1500.
|
||
|
$anvil->data->{sys}{min_usable_mtu} = 1500;
|
||
|
|
||
|
# This is the ICMP overhead to subtract from TCP payload sizes.
|
||
|
$anvil->data->{sys}{icmp_overhead} = 28;
|
||
|
$anvil->data->{sys}{icmp_mtu_stepping} = 100;
|
||
|
|
||
|
# This is used to re-check pings to avoid early failures caused by stray packets.
|
||
|
$anvil->data->{sys}{fail_count} = 0;
|
||
|
$anvil->data->{sys}{fail_limit} = 3;
|
||
|
|
||
|
# After calling iperf on the remote node, I need to wait a short time listener to be ready. This sets
|
||
|
# the delays in millionths of a second.
|
||
|
$anvil->data->{sys}{iperf_micro_sleep} = 100000;
|
||
|
|
||
|
# This is how many times I run each iperf test. An average of the results is stored.
|
||
|
$anvil->data->{sys}{iperf_test_count} = 3;
|
||
|
if ($anvil->data->{switches}{tests})
|
||
|
{
|
||
|
if (($anvil->data->{switches}{tests} =~ /^\d+$/) && ($anvil->data->{switches}{tests} > 1))
|
||
|
{
|
||
|
$anvil->data->{sys}{iperf_test_count} = $anvil->data->{switches}{tests};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::iperf_test_count" => $anvil->data->{sys}{iperf_test_count},
|
||
|
}});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "[ Error ] - The '--tests' was set to: [".$anvil->data->{switches}{tests}."] which is invalid. It must be a whole number larger than 1.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# This will store the top tested good MTU
|
||
|
$anvil->data->{sys}{highest_good_mtu} = 1500;
|
||
|
|
||
|
# Initial tests.
|
||
|
my $test_mtu = 1500;
|
||
|
my $ok = set_mtu_and_ping($anvil, $test_mtu, 0);
|
||
|
if (not $ok)
|
||
|
{
|
||
|
print "[ Error ] - Unable to run ping-based tests! Is the network up?\n";
|
||
|
return($anvil->data->{sys}{highest_good_mtu});
|
||
|
}
|
||
|
$anvil->data->{tested}{$test_mtu} = "";
|
||
|
|
||
|
# This will catch an attempt to repeat a test and break the loop.
|
||
|
my $difference = $anvil->data->{sys}{mtu_maximum} - $anvil->data->{sys}{min_usable_mtu};
|
||
|
$test_mtu = $anvil->data->{sys}{mtu_maximum};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
difference => $difference,
|
||
|
test_mtu => $test_mtu,
|
||
|
}});
|
||
|
foreach my $i (1..1000)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
's1:i' => $i,
|
||
|
's2:test_mtu' => $test_mtu,
|
||
|
}});
|
||
|
if ((exists $anvil->data->{tested}{$test_mtu}) && ($anvil->data->{tested}{$test_mtu}))
|
||
|
{
|
||
|
last;
|
||
|
}
|
||
|
|
||
|
# Test this MTU.
|
||
|
print " - Testing with an MTU of: [".$test_mtu."];\n";
|
||
|
my $ok = set_mtu_and_ping($anvil, $test_mtu, 0);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ok => $ok }});
|
||
|
|
||
|
if (($i == 1) && ($ok))
|
||
|
{
|
||
|
# Highest allowable MTU is OK.
|
||
|
$anvil->data->{sys}{highest_good_mtu} = $test_mtu;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::highest_good_mtu" => $anvil->data->{sys}{highest_good_mtu},
|
||
|
}});
|
||
|
last;
|
||
|
}
|
||
|
|
||
|
if ($ok)
|
||
|
{
|
||
|
$anvil->data->{tested}{$test_mtu} = 'ok';
|
||
|
$anvil->data->{sys}{highest_good_mtu} = $test_mtu;
|
||
|
$test_mtu = ($test_mtu + ($difference / 2**$i));
|
||
|
$test_mtu = round_to_100($anvil, $test_mtu);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"tested::$test_mtu" => $anvil->data->{tested}{$test_mtu},
|
||
|
"sys::highest_good_mtu" => $anvil->data->{sys}{highest_good_mtu},
|
||
|
test_mtu => $test_mtu,
|
||
|
}});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$anvil->data->{tested}{$test_mtu} = 'fail';
|
||
|
$test_mtu = ($test_mtu - ($difference / 2**$i));
|
||
|
$test_mtu = round_to_100($anvil, $test_mtu);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"tested::$test_mtu" => $anvil->data->{tested}{$test_mtu},
|
||
|
test_mtu => $test_mtu,
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
print "Found the highest usable MTU, rounded down to an even 100, is: [".$anvil->data->{sys}{highest_good_mtu}."].\n";
|
||
|
|
||
|
return ($anvil->data->{sys}{highest_good_mtu});
|
||
|
}
|
||
|
|
||
|
sub set_mtu_and_ping
|
||
|
{
|
||
|
my ($anvil, $test_mtu, $repeat) = @_;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
test_mtu => $test_mtu,
|
||
|
repeat => $repeat,
|
||
|
}});
|
||
|
|
||
|
my $ok = 0;
|
||
|
|
||
|
# Change the MTUs, remote first.
|
||
|
my $new_remote_mtu = alter_mtu($anvil, "remote", $test_mtu);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_remote_mtu => $new_remote_mtu }});
|
||
|
|
||
|
print " - Remote: [".$anvil->data->{sys}{target_interface}{device}."] now set with MTU: [".$new_remote_mtu."]\n";
|
||
|
if ($test_mtu ne $new_remote_mtu)
|
||
|
{
|
||
|
print "Too high.\n";
|
||
|
print "[ Note ] - I was unable to bump the MTU up to: [".$test_mtu."] on remote.\n";
|
||
|
print "[ Note ] - Still at an MTU of: [".$new_remote_mtu."].\n";
|
||
|
return($ok);
|
||
|
}
|
||
|
my $new_local_mtu = alter_mtu($anvil, "local", $test_mtu);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_local_mtu => $new_local_mtu }});
|
||
|
|
||
|
print " - Local: [".$anvil->data->{sys}{target_interface}{device}."] now set with MTU: [".$new_local_mtu."]\n";
|
||
|
if ($test_mtu ne $new_local_mtu)
|
||
|
{
|
||
|
print "Too high.\n";
|
||
|
print "[ Note ] - I was unable to bump the MTU up to: [".$test_mtu."] on remote.\n";
|
||
|
print "[ Note ] - Still at an MTU of: [".$new_remote_mtu."].\n";
|
||
|
return($ok);
|
||
|
}
|
||
|
|
||
|
my $ping_size = $test_mtu - $anvil->data->{sys}{icmp_overhead};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ping_size => $ping_size }});
|
||
|
print " - Pinging: [".$anvil->data->{switches}{target}."] with a single packet of: [".$ping_size."] bytes.\n";
|
||
|
|
||
|
my $success = ping_remote($anvil, $ping_size);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { success => $success }});
|
||
|
if ($success)
|
||
|
{
|
||
|
$ok = 1;
|
||
|
$anvil->data->{sys}{fail_count} = 0;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
ok => $ok,
|
||
|
"sys::fail_count" => $anvil->data->{sys}{fail_count},
|
||
|
}});
|
||
|
print " - ICMP payload delivered in one packet, MTU appears valid!\n";
|
||
|
print "OK!\n";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$anvil->data->{sys}{fail_count}++;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::fail_count" => $anvil->data->{sys}{fail_count},
|
||
|
}});
|
||
|
if ($anvil->data->{sys}{fail_count} > $anvil->data->{sys}{fail_limit})
|
||
|
{
|
||
|
print "Failed!\n";
|
||
|
print "[ Warning ] - ICMP payload delivery failed, MTU appears invalid!\n";
|
||
|
return($ok);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "Failed! [".$anvil->data->{sys}{fail_count}."/".$anvil->data->{sys}{fail_limit}."]\n";
|
||
|
print "[ Warning ] - ICMP payload delivery failed!\n";
|
||
|
print "[ Note ] - Failure count below limit: [".$anvil->data->{sys}{fail_count}."/".$anvil->data->{sys}{fail_limit}."].\n";
|
||
|
print "[ Note ] - Retrying in one second in case of transient network traffic.\n";
|
||
|
sleep 1;
|
||
|
$ok = set_mtu_and_ping($anvil, $test_mtu, 1);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ok => $ok }});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return($ok);
|
||
|
}
|
||
|
|
||
|
sub ping_remote
|
||
|
{
|
||
|
my ($anvil, $size) = @_;
|
||
|
|
||
|
# Do a single, non-fragmenting ping.
|
||
|
my $success = 0;
|
||
|
my $shell_call = $anvil->data->{path}{exe}{ping}." ".$anvil->data->{switches}{target}." -w 1 -M do -c 1 -s ".$size;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
foreach my $line (split/\n/, $output)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
|
||
|
if ($line =~ /0% packet loss/)
|
||
|
{
|
||
|
$success = 1;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { success => $success }});
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ($success);
|
||
|
}
|
||
|
|
||
|
sub alter_mtu
|
||
|
{
|
||
|
my ($anvil, $where, $mtu) = @_;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
where => $where,
|
||
|
mtu => $mtu,
|
||
|
}});
|
||
|
|
||
|
my $hash_key = $where eq "local" ? "source_interface" : "target_interface";
|
||
|
my $device = $anvil->data->{sys}{$hash_key}{device};
|
||
|
my $name = $anvil->data->{sys}{$hash_key}{name} ? $anvil->data->{sys}{$hash_key}{name} : $anvil->data->{sys}{$hash_key}{device};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
hash_key => $hash_key,
|
||
|
device => $device,
|
||
|
name => $name,
|
||
|
}});
|
||
|
|
||
|
my $network = "";
|
||
|
if ($device =~ /^(.*?n\d+)_/)
|
||
|
{
|
||
|
$network = $1;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network => $network }});
|
||
|
}
|
||
|
|
||
|
# Alter the MTU. To handle bonds and bridges, we'll loop through all devices with the target device's prefix (if applicable).
|
||
|
my $new_mtu = "";
|
||
|
if ($where eq "remote")
|
||
|
{
|
||
|
# Remove call.
|
||
|
if ($network)
|
||
|
{
|
||
|
# Call once for all interfaces.
|
||
|
my $host = $anvil->data->{switches}{target};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host }});
|
||
|
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$host}{interface}})
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
|
||
|
next if $interface !~ /^${network}_/;
|
||
|
|
||
|
my $name = $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME} ? $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME} : $interface;
|
||
|
my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify \"".$name."\" 802-3-ethernet.mtu ".$mtu." && ".$anvil->data->{path}{exe}{nmcli}." connection up \"".$name."\"";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $error, $return_code) = $anvil->Remote->call({
|
||
|
target => $anvil->data->{switches}{target},
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
shell_call => $shell_call,
|
||
|
});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
error => $error,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify \"".$name."\" 802-3-ethernet.mtu ".$mtu." && ".$anvil->data->{path}{exe}{nmcli}." connection up \"".$name."\"";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $error, $return_code) = $anvil->Remote->call({
|
||
|
target => $anvil->data->{switches}{target},
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
shell_call => $shell_call,
|
||
|
});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
error => $error,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
}
|
||
|
|
||
|
# Check that the MTU is now what we want
|
||
|
my $shell_call = $anvil->data->{path}{exe}{ip}." addr list ".$device;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $error, $return_code) = $anvil->Remote->call({
|
||
|
target => $anvil->data->{switches}{target},
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
shell_call => $shell_call,
|
||
|
});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
error => $error,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
|
||
|
foreach my $line (split/\n/, $output)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
|
||
|
if ($line =~ /mtu (\d+) /)
|
||
|
{
|
||
|
$new_mtu = $1;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_mtu => $new_mtu }});
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Local call
|
||
|
if ($network)
|
||
|
{
|
||
|
# Call once for all interfaces.
|
||
|
my $host = $anvil->Get->short_host_name();
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host }});
|
||
|
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$host}{interface}})
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
|
||
|
next if $interface !~ /^${network}_/;
|
||
|
|
||
|
my $name = $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME} ? $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME} : $interface;
|
||
|
my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify \"".$name."\" 802-3-ethernet.mtu ".$mtu." && ".$anvil->data->{path}{exe}{nmcli}." connection up \"".$name."\"";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
my $shell_call = $anvil->data->{path}{exe}{nmcli}." connection modify \"".$name."\" 802-3-ethernet.mtu ".$mtu." && ".$anvil->data->{path}{exe}{nmcli}." connection up \"".$name."\"";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
}
|
||
|
|
||
|
my $shell_call = $anvil->data->{path}{exe}{ip}." addr list ".$device;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
|
||
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
|
||
|
foreach my $line (split/\n/, $output)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
|
||
|
if ($line =~ /mtu (\d+) /)
|
||
|
{
|
||
|
$new_mtu = $1;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_mtu => $new_mtu }});
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
new_mtu => $new_mtu,
|
||
|
mtu => $mtu,
|
||
|
}});
|
||
|
if ($new_mtu ne $mtu)
|
||
|
{
|
||
|
# Failed.
|
||
|
print "[ Warning ] - Failed to set the ".$where." device: [".$device."] to have an MTU of: [".$mtu."]\n";
|
||
|
print "[ Warning ] - Detected current MTU as: [".$new_mtu."]\n";
|
||
|
}
|
||
|
|
||
|
return ($new_mtu);
|
||
|
}
|
||
|
|
||
|
sub round_to_100
|
||
|
{
|
||
|
my ($anvil, $number) = @_;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { number => $number }});
|
||
|
|
||
|
$number = sprintf("%.0f", $number);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { number => $number }});
|
||
|
|
||
|
my $diff = ($number % 100);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { diff => $diff }});
|
||
|
if ($diff >= 50)
|
||
|
{
|
||
|
$number += (100 - $diff);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { number => $number }});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$number -= $diff;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { number => $number }});
|
||
|
}
|
||
|
|
||
|
return ($number);
|
||
|
}
|
||
|
|
||
|
sub checks
|
||
|
{
|
||
|
my ($anvil) = @_;
|
||
|
|
||
|
if ((not $anvil->data->{switches}{source}) or (not $anvil->data->{switches}{target}))
|
||
|
{
|
||
|
print "Usage: ".$THIS_FILE." --source <local ip> --target <peer IP>\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
|
||
|
if (not $anvil->Validate->ip({ip => $anvil->data->{switches}{source}}))
|
||
|
{
|
||
|
print "The source IP: [".$anvil->data->{switches}{source}."] does not appear to be valid.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
|
||
|
if (not $anvil->Validate->ip({ip => $anvil->data->{switches}{target}}))
|
||
|
{
|
||
|
print "The target IP: [".$anvil->data->{switches}{target}."] does not appear to be valid.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
|
||
|
my $access = $anvil->Remote->test_access({
|
||
|
target => $anvil->data->{switches}{target},
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }});
|
||
|
if ($access)
|
||
|
{
|
||
|
if (not $anvil->data->{switches}{y})
|
||
|
{
|
||
|
print "[ OK ] - Access to: [".$anvil->data->{switches}{target}."] confirmed.\n";
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ($anvil->data->{switches}{password})
|
||
|
{
|
||
|
print "Failed to log into the target: [".$anvil->data->{switches}{target}."]. Was the password correct?\n";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "Failed to log into the target: [".$anvil->data->{switches}{target}."]. If passwordless access is not configured, you can set the password with '--passord <secret>'.\n";
|
||
|
}
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
|
||
|
if (($anvil->data->{switches}{'max-mtu'}) && (($anvil->data->{switches}{'max-mtu'} =~ /\D/) or ($anvil->data->{switches}{'max-mtu'} < 1500)))
|
||
|
{
|
||
|
print "The maximum MTU size: [".$anvil->data->{switches}{'max-mtu'}."] is invalid. It must be a whole number greater than 1500.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
|
||
|
# Make sure 'iperf3' is installed on both machines.
|
||
|
if (not -e $anvil->data->{path}{exe}{iperf3})
|
||
|
{
|
||
|
print "It appears that 'iperf3' is not installed on this system.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
my $shell_call = "if [ -e '".$anvil->data->{path}{exe}{iperf3}."' ]; then echo installed; else echo missing; fi";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
|
||
|
my ($output, $error, $return_code) = $anvil->Remote->call({
|
||
|
target => $anvil->data->{switches}{target},
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
shell_call => $shell_call,
|
||
|
});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
output => $output,
|
||
|
error => $error,
|
||
|
return_code => $return_code,
|
||
|
}});
|
||
|
if ($output ne "installed")
|
||
|
{
|
||
|
print "It appears that 'iperf3' is not installed on this target.\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
if (not $anvil->data->{switches}{y})
|
||
|
{
|
||
|
print "[ OK ] - Both source and target have 'iperf3' installed'\n";
|
||
|
}
|
||
|
|
||
|
# Get the device name of the interfaces with the IPs.
|
||
|
my $host_name = $anvil->Get->short_host_name();
|
||
|
my $target_ip = $anvil->data->{switches}{target};
|
||
|
$anvil->Network->get_ips({target => $host_name});
|
||
|
$anvil->Network->get_ips({
|
||
|
target => $target_ip,
|
||
|
password => $anvil->data->{switches}{password},
|
||
|
});
|
||
|
$anvil->data->{sys}{source_interface}{device} = "";
|
||
|
$anvil->data->{sys}{source_interface}{name} = "";
|
||
|
$anvil->data->{sys}{target_interface}{device} = "";
|
||
|
$anvil->data->{sys}{target_interface}{name} = "";
|
||
|
foreach my $host ($host_name, $target_ip)
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host => $host }});
|
||
|
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$host}{interface}})
|
||
|
{
|
||
|
my $this_ip = $anvil->data->{network}{$host}{interface}{$interface}{ip};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { this_ip => $this_ip }});
|
||
|
if ($this_ip eq $anvil->data->{switches}{source})
|
||
|
{
|
||
|
$anvil->data->{sys}{source_interface}{device} = $interface;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::source_interface::device" => $anvil->data->{sys}{source_interface}{device},
|
||
|
}});
|
||
|
|
||
|
if (exists $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME})
|
||
|
{
|
||
|
$anvil->data->{sys}{source_interface}{name} = $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::source_interface::name" => $anvil->data->{sys}{source_interface}{name},
|
||
|
}});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$anvil->data->{sys}{source_interface}{name} = $interface;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::source_interface::name" => $anvil->data->{sys}{source_interface}{name},
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
elsif ($this_ip eq $anvil->data->{switches}{target})
|
||
|
{
|
||
|
$anvil->data->{sys}{target_interface}{device} = $interface;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::target_interface::device" => $anvil->data->{sys}{target_interface}{device},
|
||
|
}});
|
||
|
|
||
|
if (exists $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME})
|
||
|
{
|
||
|
$anvil->data->{sys}{target_interface}{name} = $anvil->data->{network}{$host}{interface}{$interface}{variable}{NAME};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::target_interface::name" => $anvil->data->{sys}{target_interface}{name},
|
||
|
}});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$anvil->data->{sys}{target_interface}{name} = $interface;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"sys::target_interface::name" => $anvil->data->{sys}{target_interface}{name},
|
||
|
}});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (not $anvil->data->{sys}{source_interface}{name})
|
||
|
{
|
||
|
print "Unable to find the local network interface with the IP: [".$anvil->data->{switches}{source}."].\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
if (not $anvil->data->{sys}{target_interface}{name})
|
||
|
{
|
||
|
print "Unable to find the target's network interface with the IP: [".$anvil->data->{switches}{target}."].\n";
|
||
|
$anvil->nice_exit({exit_code => 1});
|
||
|
}
|
||
|
|
||
|
# Ask if we can proceed.
|
||
|
if (not $anvil->data->{switches}{y})
|
||
|
{
|
||
|
print "=============================================================================================================
|
||
|
[ Warning ] - This test will alter the MTU sizes of the network interfaces. In some cases, this could cause
|
||
|
the network to stop working. Any applications that rely on the network could fail (like a
|
||
|
cluster). Do you have direct access to this machine and the target machine to reset the MTU if
|
||
|
needed? This test can take up to half an hour to run!
|
||
|
=============================================================================================================
|
||
|
[ Note ] - You can skip this prompt by using '--y'.
|
||
|
[ Note ] - You can set the maximum MTU to test with '--max-mtu <bytes>'.
|
||
|
- Are you ready to proceed? [n/Y]
|
||
|
";
|
||
|
my $answer = <STDIN>;
|
||
|
chomp $answer;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }});
|
||
|
|
||
|
if ((lc($answer) ne "y") && (lc($answer) ne "yes"))
|
||
|
{
|
||
|
print "Aborting.\n";
|
||
|
$anvil->nice_exit({exit_code => 0});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|