Subversion Repositories havirt

Rev

Rev 46 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 46 Rev 47
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";