| Line 33... |
Line 33... |
| 33 |
# (new entry in config file). --dryrun will simply display the commands sent, and --nodryrun will execute them.
|
33 |
# (new entry in config file). --dryrun will simply display the commands sent, and --nodryrun will execute them.
|
| 34 |
#
|
34 |
#
|
| 35 |
# v1.3.0 20250514 RWR
|
35 |
# v1.3.0 20250514 RWR
|
| 36 |
# Modified so it will not issue error codes if we have done iterations but not perfectly in balance. Also, added message
|
36 |
# Modified so it will not issue error codes if we have done iterations but not perfectly in balance. Also, added message
|
| 37 |
# 'already in balance' if it is as good as we can get it.
|
37 |
# 'already in balance' if it is as good as we can get it.
|
| 38 |
#
|
38 |
#
|
| - |
|
39 |
# v1.3.2 20260102 RWR
|
| - |
|
40 |
# Refactored all command executions to use centralized execute() function
|
| - |
|
41 |
# Updated updateISCITargets function to use execute() for all iscsiadm commands
|
| 39 |
|
42 |
|
| 40 |
|
43 |
|
| 41 |
|
44 |
|
| 42 |
package cluster;
|
45 |
package cluster;
|
| 43 |
|
46 |
|
| Line 45... |
Line 48... |
| 45 |
use strict;
|
48 |
use strict;
|
| 46 |
|
49 |
|
| 47 |
# define the version number
|
50 |
# define the version number
|
| 48 |
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
|
51 |
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
|
| 49 |
use version;
|
52 |
use version;
|
| 50 |
our $VERSION = version->declare("1.3.1");
|
53 |
our $VERSION = version->declare("1.3.2");
|
| 51 |
|
54 |
|
| 52 |
|
55 |
|
| 53 |
use Data::Dumper;
|
56 |
use Data::Dumper;
|
| 54 |
|
57 |
|
| 55 |
use Exporter;
|
58 |
use Exporter;
|
| Line 179... |
Line 182... |
| 179 |
push @return, "Processing iSCSI targets on $node";
|
182 |
push @return, "Processing iSCSI targets on $node";
|
| 180 |
print Dumper( keys %{ $main::statusDB->{'cluster'}->{'iscsi'} } ) if $main::config->{'flags'}->{'debug'};
|
183 |
print Dumper( keys %{ $main::statusDB->{'cluster'}->{'iscsi'} } ) if $main::config->{'flags'}->{'debug'};
|
| 181 |
foreach my $server (keys %{ $main::statusDB->{'cluster'}->{'iscsi'} } ) {
|
184 |
foreach my $server (keys %{ $main::statusDB->{'cluster'}->{'iscsi'} } ) {
|
| 182 |
print "\n" . '-'x40 . "\nGetting targets on server $server\n" . '-'x40 . "\n" if $main::config->{'flags'}->{'verbose'};
|
185 |
print "\n" . '-'x40 . "\nGetting targets on server $server\n" . '-'x40 . "\n" if $main::config->{'flags'}->{'verbose'};
|
| 183 |
$command = &main::makeCommand( $node, "iscsiadm -m discovery -t st -p $server" );
|
186 |
$command = &main::makeCommand( $node, "iscsiadm -m discovery -t st -p $server" );
|
| 184 |
my @list = `$command`;
|
187 |
my @list = &main::execute($command);
|
| 185 |
chomp @list;
|
188 |
chomp @list;
|
| 186 |
# @list contains lines of type
|
189 |
# @list contains lines of type
|
| 187 |
# 10.19.209.2:3260,1 iqn.2014-11.net.dailydata.castor:simon0
|
190 |
# 10.19.209.2:3260,1 iqn.2014-11.net.dailydata.castor:simon0
|
| 188 |
# split them apart and add them to the hash
|
191 |
# split them apart and add them to the hash
|
| 189 |
foreach my $entry ( @list ) {
|
192 |
foreach my $entry ( @list ) {
|
| Line 198... |
Line 201... |
| 198 |
} # foreach
|
201 |
} # foreach
|
| 199 |
} # while
|
202 |
} # while
|
| 200 |
print "\n" . '-'x40 . "\nGetting active sessions\n". '-'x40 . "\n" if $main::config->{'flags'}->{'verbose'};
|
203 |
print "\n" . '-'x40 . "\nGetting active sessions\n". '-'x40 . "\n" if $main::config->{'flags'}->{'verbose'};
|
| 201 |
# now, get active sessions so we can filter them
|
204 |
# now, get active sessions so we can filter them
|
| 202 |
$command = &main::makeCommand( $node, "iscsiadm -m session" );
|
205 |
$command = &main::makeCommand( $node, "iscsiadm -m session" );
|
| 203 |
my @activeSessions = `$command`;;
|
206 |
my @activeSessions = &main::execute($command);
|
| 204 |
chomp @activeSessions;
|
207 |
chomp @activeSessions;
|
| 205 |
foreach my $session ( @activeSessions ) {
|
208 |
foreach my $session ( @activeSessions ) {
|
| 206 |
$session =~ m/^.*[^0-9:.]([0-9,:.]+).*(iqn\S*)/;
|
209 |
$session =~ m/^.*[^0-9:.]([0-9,:.]+).*(iqn\S*)/;
|
| 207 |
my ( $portal,$targetName ) = ( $1,$2 );
|
210 |
my ( $portal,$targetName ) = ( $1,$2 );
|
| 208 |
print "$portal\t$targetName" if $main::config->{'flags'}->{'verbose'};
|
211 |
print "$portal\t$targetName" if $main::config->{'flags'}->{'verbose'};
|
| Line 222... |
Line 225... |
| 222 |
push @return, "Adding $targetName";
|
225 |
push @return, "Adding $targetName";
|
| 223 |
$command = &main::makeCommand( $node, "iscsiadm -m node --targetname '$targetName' --portal '$portal' --login" );
|
226 |
$command = &main::makeCommand( $node, "iscsiadm -m node --targetname '$targetName' --portal '$portal' --login" );
|
| 224 |
if ( $main::config->{'flags'}->{'dryrun'} ) {
|
227 |
if ( $main::config->{'flags'}->{'dryrun'} ) {
|
| 225 |
push @return, $command;
|
228 |
push @return, $command;
|
| 226 |
} else {
|
229 |
} else {
|
| 227 |
`$command`;
|
230 |
&main::execute($command);
|
| 228 |
}
|
231 |
}
|
| 229 |
}
|
232 |
}
|
| 230 |
} else {
|
233 |
} else {
|
| 231 |
push @return, "No new entries";
|
234 |
push @return, "No new entries";
|
| 232 |
}
|
235 |
}
|
| Line 272... |
Line 275... |
| 272 |
$return->{'cluster'}->{'used_memory'} += $return->{'node'}->{$node}->{'used_memory'};
|
275 |
$return->{'cluster'}->{'used_memory'} += $return->{'node'}->{$node}->{'used_memory'};
|
| 273 |
$return->{'cluster'}->{'used_vcpu'} += $return->{'node'}->{$node}->{'used_vcpu'};
|
276 |
$return->{'cluster'}->{'used_vcpu'} += $return->{'node'}->{$node}->{'used_vcpu'};
|
| 274 |
$return->{'cluster'}->{'domain_count'} += $return->{'node'}->{$node}->{'count'};
|
277 |
$return->{'cluster'}->{'domain_count'} += $return->{'node'}->{$node}->{'count'};
|
| 275 |
}
|
278 |
}
|
| 276 |
# calculate the deviation for each active node in the cluster
|
279 |
# calculate the deviation for each active node in the cluster
|
| 277 |
$return->{'cluster'}->{'average_memory'} = $return->{'cluster'}->{'used_memory'} / $return->{'cluster'}->{'memory'};
|
280 |
$return->{'cluster'}->{'average_memory'} = $return->{'cluster'}->{'used_memory'} / $return->{'cluster'}->{'memory'} if $return->{'cluster'}->{'memory'};
|
| 278 |
|
281 |
|
| 279 |
# get the deviation for each node
|
282 |
# get the deviation for each node
|
| 280 |
# variance in the cluster is simply the average of all deviations
|
283 |
# variance in the cluster is simply the average of all deviations
|
| 281 |
$return->{'cluster'}->{'variance'} = 0;
|
284 |
$return->{'cluster'}->{'variance'} = 0;
|
| - |
|
285 |
$return->{'cluster'}->{'activeNodes'} = 0;
|
| 282 |
foreach my $node (sort keys %{ $main::statusDB->{'node'} } ) {
|
286 |
foreach my $node (sort keys %{ $main::statusDB->{'node'} } ) {
|
| 283 |
# deviation is the square of the difference between this node and the cluster overall
|
287 |
# deviation is the square of the difference between this node and the cluster overall
|
| 284 |
$return->{'node'}->{$node}->{'deviation'} = (
|
288 |
$return->{'node'}->{$node}->{'deviation'} = (
|
| 285 |
$return->{'node'}->{$node}->{'average_memory'} / $return->{'cluster'}->{'average_memory'}
|
289 |
$return->{'node'}->{$node}->{'average_memory'} / $return->{'cluster'}->{'average_memory'}
|
| 286 |
) ** 2;
|
290 |
) ** 2;
|
| 287 |
# we'll divide by number of active nodes after the loop
|
291 |
# we'll divide by number of active nodes after the loop
|
| 288 |
$return->{'cluster'}->{'variance'} += $return->{'node'}->{$node}->{'deviation'};
|
292 |
$return->{'cluster'}->{'variance'} += $return->{'node'}->{$node}->{'deviation'};
|
| - |
|
293 |
# do not count nodes in maintenance mode as active
|
| - |
|
294 |
$return->{'cluster'}->{'activeNodes'}++ unless $main::statusDB->{'node'}->{$node}->{'maintenance'};
|
| 289 |
}
|
295 |
}
|
| - |
|
296 |
die "No active nodes in cluster\n" unless $return->{'cluster'}->{'activeNodes'};
|
| 290 |
$return->{'cluster'}->{'variance'} /= $return->{'cluster'}->{'count'};
|
297 |
# die "$return->{'cluster'}->{'variance'}\t$return->{'cluster'}->{'activeNodes'}\n";
|
| - |
|
298 |
$return->{'cluster'}->{'variance'} /= $return->{'cluster'}->{'activeNodes'} if $return->{'cluster'}->{'activeNodes'};
|
| 291 |
# now, determine how much memory needs to be added (plus) or removed (minus) for each node
|
299 |
# now, determine how much memory needs to be added (plus) or removed (minus) for each node
|
| 292 |
# memory_needed is calculated by taking the total amount of memory and multiplying it by the cluster average memory
|
300 |
# memory_needed is calculated by taking the total amount of memory and multiplying it by the cluster average memory
|
| 293 |
# then subtracting whatever is already used
|
301 |
# then subtracting whatever is already used
|
| 294 |
foreach my $node (sort keys %{ $main::statusDB->{'node'} } ) {
|
302 |
foreach my $node (sort keys %{ $main::statusDB->{'node'} } ) {
|
| 295 |
if ( $main::statusDB->{'node'}->{$node}->{'maintenance'} ) {
|
303 |
if ( $main::statusDB->{'node'}->{$node}->{'maintenance'} ) {
|
| Line 389... |
Line 397... |
| 389 |
# get the current cluster status
|
397 |
# get the current cluster status
|
| 390 |
my $cluster = &getClusterStats();
|
398 |
my $cluster = &getClusterStats();
|
| 391 |
#die Dumper( $cluster ) . "\n";
|
399 |
#die Dumper( $cluster ) . "\n";
|
| 392 |
# show user what it looks like at first
|
400 |
# show user what it looks like at first
|
| 393 |
print "=== Starting Status ===\n\n" . &showBalanceReport( $cluster) unless $main::config->{'flags'}->{'quiet'};
|
401 |
print "=== Starting Status ===\n\n" . &showBalanceReport( $cluster) unless $main::config->{'flags'}->{'quiet'};
|
| - |
|
402 |
|
| - |
|
403 |
# First, ensure all domains are removed from nodes in maintenance mode
|
| - |
|
404 |
foreach my $node (keys %{$main::statusDB->{'node'}}) {
|
| - |
|
405 |
if ( $main::statusDB->{'node'}->{$node}->{'maintenance'} ) {
|
| - |
|
406 |
my @domains = keys %{$main::statusDB->{'nodePopulation'}->{$node}->{'running'}};
|
| - |
|
407 |
if ( @domains ) {
|
| - |
|
408 |
print "Node $node is in maintenance mode with " . scalar(@domains) . " domains, evacuating...\n"
|
| - |
|
409 |
if $main::config->{'flags'}->{'verbose'};
|
| - |
|
410 |
foreach my $domain (@domains) {
|
| - |
|
411 |
# Skip domains that are also in maintenance
|
| - |
|
412 |
next if $main::statusDB->{'virt'}->{$domain}->{'maintenance'};
|
| - |
|
413 |
# Find a suitable target node (not in maintenance, with enough resources)
|
| - |
|
414 |
my $target = '';
|
| - |
|
415 |
foreach my $candidate (keys %{$main::statusDB->{'node'}}) {
|
| - |
|
416 |
next if $main::statusDB->{'node'}->{$candidate}->{'maintenance'};
|
| - |
|
417 |
next if $candidate eq $node;
|
| - |
|
418 |
# Check if target has enough resources
|
| - |
|
419 |
my $available = &main::getAvailableResources($candidate);
|
| - |
|
420 |
if ($available->{'memory'} >= $main::statusDB->{'virt'}->{$domain}->{'memory'}) {
|
| - |
|
421 |
$target = $candidate;
|
| - |
|
422 |
last;
|
| - |
|
423 |
}
|
| - |
|
424 |
}
|
| - |
|
425 |
if ($target) {
|
| - |
|
426 |
$return .= &main::migrate($domain, $target, $node);
|
| - |
|
427 |
delete $main::statusDB->{'nodePopulation'}->{$node}->{'running'}->{$domain};
|
| - |
|
428 |
$main::statusDB->{'nodePopulation'}->{$target}->{'running'}->{$domain} = time;
|
| - |
|
429 |
} else {
|
| - |
|
430 |
print "Warning: Could not find suitable target for $domain from maintenance node $node\n";
|
| - |
|
431 |
}
|
| - |
|
432 |
}
|
| - |
|
433 |
&main::forceScan() unless $main::config->{'flags'}->{'dryrun'} || $main::config->{'flags'}->{'testing'};
|
| - |
|
434 |
# Refresh cluster stats after evacuation
|
| - |
|
435 |
$cluster = &getClusterStats();
|
| - |
|
436 |
}
|
| - |
|
437 |
}
|
| - |
|
438 |
}
|
| - |
|
439 |
|
| 394 |
# we will do a loop to get the variance within our preferred range ($main::config->{ 'balance variance'})
|
440 |
# we will do a loop to get the variance within our preferred range ($main::config->{ 'balance variance'})
|
| 395 |
# however, we will only do a maximum number of iterations ($main::config->{ 'balance maxiterations'})
|
441 |
# however, we will only do a maximum number of iterations ($main::config->{ 'balance maxiterations'})
|
| 396 |
my $iterations = defined $main::config->{ 'balance_max_iterations'} && $main::config->{ 'balance_max_iterations'} ? $main::config->{ 'balance_max_iterations'} : 10;
|
442 |
my $iterations = defined $main::config->{ 'balance_max_iterations'} && $main::config->{ 'balance_max_iterations'} ? $main::config->{ 'balance_max_iterations'} : 10;
|
| 397 |
$main::config->{ 'balance_max_variance'} = 1.1 unless defined $main::config->{ 'balance_max_variance'};
|
443 |
$main::config->{ 'balance_max_variance'} = 0.5 unless defined $main::config->{ 'balance_max_variance'};
|
| 398 |
# continue until our variance is where we want it, or we have tried too many times.
|
444 |
# continue until our variance is where we want it, or we have tried too many times.
|
| 399 |
while ( $iterations-- && $cluster->{'cluster'}->{'variance'} > $main::config->{ 'balance_max_variance'} ) {
|
445 |
while ( $iterations-- && $cluster->{'cluster'}->{'variance'} > $main::config->{ 'balance_max_variance'} ) {
|
| 400 |
my $actions = &moveThings( $cluster );
|
446 |
my $actions = &moveThings( $cluster );
|
| 401 |
if ( my $output = &doActions( $actions ) ) {
|
447 |
if ( my $output = &doActions( $actions ) ) {
|
| 402 |
$return .= $output;
|
448 |
$return .= $output;
|
| Line 425... |
Line 471... |
| 425 |
my $transfer;
|
471 |
my $transfer;
|
| 426 |
my $from = '';
|
472 |
my $from = '';
|
| 427 |
my $to = '';
|
473 |
my $to = '';
|
| 428 |
# find smallest and largest "memory needed" in group. Note that if a node has too much, the number is negative and
|
474 |
# find smallest and largest "memory needed" in group. Note that if a node has too much, the number is negative and
|
| 429 |
# for too little (ie, needs additional), the number is positive
|
475 |
# for too little (ie, needs additional), the number is positive
|
| - |
|
476 |
# Skip nodes in maintenance mode for the target, but maintenance nodes can be source
|
| 430 |
foreach my $node (keys %{$stats->{'node'} } ) {
|
477 |
foreach my $node (keys %{$stats->{'node'} } ) {
|
| 431 |
#print "Checking $node\n";
|
478 |
#print "Checking $node\n";
|
| 432 |
if ( $from ) {
|
479 |
if ( $from ) {
|
| 433 |
$from = $node if $stats->{'node'}->{$from}->{'memory_needed'} > $stats->{'node'}->{$node}->{'memory_needed'};
|
480 |
$from = $node if $stats->{'node'}->{$from}->{'memory_needed'} > $stats->{'node'}->{$node}->{'memory_needed'};
|
| - |
|
481 |
# Only consider non-maintenance nodes as targets
|
| - |
|
482 |
if ( !$stats->{'node'}->{$node}->{'maintenance'} ) {
|
| - |
|
483 |
if ( $to ) {
|
| 434 |
$to = $node if $stats->{'node'}->{$to}->{'memory_needed'} < $stats->{'node'}->{$node}->{'memory_needed'};
|
484 |
$to = $node if $stats->{'node'}->{$to}->{'memory_needed'} < $stats->{'node'}->{$node}->{'memory_needed'};
|
| - |
|
485 |
} else {
|
| - |
|
486 |
$to = $node;
|
| - |
|
487 |
}
|
| - |
|
488 |
}
|
| 435 |
} else { # just initialize everything to this node
|
489 |
} else { # initialize $from to this node, $to only if not in maintenance
|
| 436 |
$from = $to = $node;
|
490 |
$from = $node;
|
| - |
|
491 |
$to = $node unless $stats->{'node'}->{$node}->{'maintenance'};
|
| 437 |
} #if .. else
|
492 |
} #if .. else
|
| 438 |
} # foreach
|
493 |
} # foreach
|
| - |
|
494 |
|
| - |
|
495 |
# Don't move anything if no valid target nodes available
|
| - |
|
496 |
return $actions unless $to;
|
| - |
|
497 |
|
| 439 |
# this is a poor mans min. we want to transfer the least number of bytes, ie what $from can spare, or what $to can accept
|
498 |
# this is a poor mans min. we want to transfer the least number of bytes, ie what $from can spare, or what $to can accept
|
| 440 |
# we need the smallest of what $from can give and $to can accept
|
499 |
# we need the smallest of what $from can give and $to can accept
|
| 441 |
$transfer = abs( abs( $stats->{'node'}->{$from}->{'memory_needed'} ) > abs( $stats->{'node'}->{$to}->{'memory_needed'} ) ?
|
500 |
$transfer = abs( abs( $stats->{'node'}->{$from}->{'memory_needed'} ) > abs( $stats->{'node'}->{$to}->{'memory_needed'} ) ?
|
| 442 |
$stats->{'node'}->{$to}->{'memory_needed'} : $stats->{'node'}->{$from}->{'memory_needed'} );
|
501 |
$stats->{'node'}->{$to}->{'memory_needed'} : $stats->{'node'}->{$from}->{'memory_needed'} );
|
| 443 |
# die "Transfer " . &humanReadable($transfer) ." bytes from $from to $to\n";
|
502 |
# die "Transfer " . &humanReadable($transfer) ." bytes from $from to $to\n";
|