This allow to rebalance a node, with defining max mem/cpu threshold.

we migrate vms until we are under threshold.

Signed-off-by: Alexandre Derumier <[email protected]>
---
 PVE/API2/Nodes.pm | 130 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 127 insertions(+), 3 deletions(-)

diff --git a/PVE/API2/Nodes.pm b/PVE/API2/Nodes.pm
index 25199249..35e138c7 100644
--- a/PVE/API2/Nodes.pm
+++ b/PVE/API2/Nodes.pm
@@ -2010,6 +2010,130 @@ __PACKAGE__->register_method ({
 
     }});
 
+__PACKAGE__->register_method ({
+    name => 'rebalance',
+    path => 'rebalance',
+    method => 'POST',
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'VM.Migrate' ]],
+    },
+    description => "Rebalance VMs and Containers.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+            mem_threshold => {
+                description => "target mem % threshold",
+                type => 'integer',
+                minimum => 1
+            },
+            cpu_threshold => {
+                description => "target mem % threshold",
+                type => 'integer',
+                minimum => 1
+            },
+       },
+    },
+    returns => {
+       type => 'string',
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+       my $nodename = $param->{node};
+       $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
+
+       my $mem_threshold = $param->{mem_threshold};
+       my $cpu_threshold = $param->{cpu_threshold};
+
+       PVE::Cluster::check_cfs_quorum();
+
+       my $members = PVE::Cluster::get_members();
+       my $maxWorkers = 1;
+       my $storecfg = PVE::Storage::config();
+
+       my $code = sub {
+           $rpcenv->{type} = 'priv'; # to start tasks in background
+
+           my $vmlist = &$get_filtered_vmlist($nodename, undef, 0, 1);
+           my $workers = {};
+
+           my $rrd = PVE::Cluster::rrd_dump();
+
+           my $node_stats = PVE::API2Tools::extract_node_stats($nodename, 
$members, $rrd);
+           my $node_mem_pct = $node_stats->{mem} / $node_stats->{maxmem} * 100;
+           my $node_cpu_pct = $node_stats->{cpu} / $node_stats->{maxcpu};
+
+           return if ($node_mem_pct < $mem_threshold && $node_cpu_pct < 
$cpu_threshold);
+
+           my $vmlist_stats = {};
+           foreach my $vmid (sort keys %$vmlist) {
+                my $d = $vmlist->{$vmid};
+               my $vmconf = PVE::QemuConfig->load_config($vmid);
+               my $vm_stats = PVE::API2Tools::extract_vm_stats($vmid, $d, 
$rrd);
+               my $vm_cpu = $vm_stats->{cpu} * $vm_stats->{maxcpu};
+               my $vm_mem = $vm_stats->{mem};
+               $vmlist_stats->{$vmid}->{cpu} = $vm_cpu;
+               $vmlist_stats->{$vmid}->{mem} = $vm_mem;
+           }
+
+           my @vmlist_array;
+           #order vmlist with bigger usage to reduce number of vm migration
+           if ($node_mem_pct < $mem_threshold && $node_cpu_pct < 
$cpu_threshold) {
+               #order by cpu, then ram ?
+               @vmlist_array = sort { $a->{mem} <=> $b->{mem} || $a->{cpu} <=> 
$b->{cpu} } keys %$vmlist_stats;
+           } elsif ($node_mem_pct < $mem_threshold) {
+               #order by mem usage first
+               @vmlist_array = sort { $a->{mem} <=> $b->{mem} } keys 
%$vmlist_stats;
+           } elsif ($node_cpu_pct < $cpu_threshold) {
+               #order by cpu usage first
+               @vmlist_array = sort { $a->{cpu} <=> $b->{cpu} } keys 
%$vmlist_stats;
+           }
+
+           foreach my $vmid (@vmlist_array) {
+               my $d = $vmlist->{$vmid};
+               my $target = find_best_node_target($vmid, $d, $nodename, 
$storecfg, $mem_threshold/100, $cpu_threshold/100);
+               if(!$target) {
+                   warn "couldn't find a target for vmid $vmid\n";
+                   next;
+               }
+
+               my $pid;
+               eval { $pid = &$create_migrate_worker($nodename, $d->{type}, 
$vmid, $target); };
+               $target = $param->{target};
+               warn $@ if $@;
+               next if !$pid;
+
+               $workers->{$pid} = 1;
+               while (scalar(keys %$workers) >= $maxWorkers) {
+                   foreach my $p (keys %$workers) {
+                       if (!PVE::ProcFSTools::check_process_running($p)) {
+                           delete $workers->{$p};
+                       }
+                   }
+                   sleep(1);
+               }
+           }
+           while (scalar(keys %$workers)) {
+               foreach my $p (keys %$workers) {
+                   if (!PVE::ProcFSTools::check_process_running($p)) {
+                       delete $workers->{$p};
+                   }
+               }
+               sleep(1);
+           }
+           return;
+       };
+
+       return $rpcenv->fork_worker('rebalance', undef, $authuser, $code);
+
+    }});
+
 __PACKAGE__->register_method ({
     name => 'get_etc_hosts',
     path => 'hosts',
@@ -2123,14 +2247,14 @@ sub dotprod {
 }
 
 sub find_best_node_target {
-    my($vmid, $d, $nodename, $storecfg) = @_;
+    my($vmid, $d, $nodename, $storecfg, $mem_threshold, $cpu_threshold) = @_;
 
     my $vmconf = PVE::QemuConfig->load_config($vmid);
     my $members = PVE::Cluster::get_members();
     my $rrd = PVE::Cluster::rrd_dump();
     my $nodelist = PVE::Cluster::get_nodelist();
-    my $mem_threshold = 0.8;
-    my $cpu_threshold = 0.8;
+    $mem_threshold = 0.8 if !$mem_threshold;
+    $cpu_threshold = 0.8 if !$cpu_threshold;
     my $vm_stats = PVE::API2Tools::extract_vm_stats($vmid, $d, $rrd);
     my $vm_cpu = $vm_stats->{cpu} * $vm_stats->{maxcpu};
     my $vm_mem = $vm_stats->{mem};
-- 
2.20.1

_______________________________________________
pve-devel mailing list
[email protected]
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to