[pve-devel] [PATCH v2 pve-manager 7/7] add node rebalance api
Alexandre Derumier
aderumier at odiso.com
Wed Oct 9 16:01:37 CEST 2019
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 <aderumier at odiso.com>
---
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
More information about the pve-devel
mailing list