PVE::Cluster::broadcast_rrd("pve2-node/$nodename", $data);
}
+sub auto_balloning {
+ my ($vmstatus) = @_;
+
+ my $log = sub {
+ return if !$opt_debug;
+ print @_;
+ };
+ my $hostmeminfo = PVE::ProcFSTools::read_meminfo();
+
+ # to debug, run 'pvestatd -d' and set memtotal here
+ #$hostmeminfo->{memtotal} = int(3*1024*1024*1024/0.8); # you can set this to test
+
+ my $hostfreemem = $hostmeminfo->{memtotal} - $hostmeminfo->{memused};
+
+ # we try to use about 80% host memory
+ # goal: we want to change memory usage by this amount (positive or negative)
+ my $goal = int($hostmeminfo->{memtotal}*0.8 - $hostmeminfo->{memused});
+
+ &$log("host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
+
+ my $maxchange = 100*1024*1024;
+
+ my $get_summary = sub {
+ my ($idlist) = @_;
+
+ my $shares = 0;
+ my $freeshares = 0;
+ my $alloc = 0;
+ my $free = 0;
+ foreach my $vmid (@$idlist) {
+ my $d = $vmstatus->{$vmid};
+ $shares += $d->{shares} || 1000;
+ $freeshares += 1/($d->{shares} || 1000);
+ if ($d->{balloon} > $d->{balloon_min}) { # just to be sure
+ $alloc += $d->{balloon} - $d->{balloon_min}
+ }
+ if ($d->{maxmem} > $d->{balloon}) { # just to be sure
+ $free += $d->{maxmem} - $d->{balloon};
+ }
+ }
+ return ($shares, $freeshares, $alloc, $free);
+ };
+
+ my $grow_func = sub {
+ my ($res, $idlist, $bytes) = @_;
+
+ my $changes = 0;
+ my (undef, $shares_total, undef, $free_total) = &$get_summary($idlist);
+ return $changes if !$shares_total;
+
+ &$log("grow $goal\n");
+
+ my $target = $bytes < $free_total ? $free_total - $bytes : 0;
+ &$log("shares_total: $shares_total\n");
+ &$log("free_total: $free_total\n");
+ &$log("target: $target\n");
+
+ foreach my $vmid (@$idlist) {
+ my $d = $vmstatus->{$vmid};
+ my $shares = 1/($d->{shares} || 1000);
+ &$log("shares $vmid: $shares\n");
+ next if $shares < 0; # just to be sure
+ my $max = $d->{maxmem} - int(($target/$shares_total)*$shares);
+ $max = $d->{balloon_min} if $max < $d->{balloon_min};
+ my $new = $d->{balloon} + $maxchange;
+ my $balloon = $new > $max ? $max : $new;
+ my $diff = $balloon - $d->{balloon};
+ if ($diff > 0) {
+ $res->{$vmid} = $balloon;
+ $changes += $diff;
+ &$log("grow request for $vmid ($res->{$vmid}, $diff, $max, $new)\n");
+ }
+ }
+ return $changes;
+ };
+
+ my $idlist = []; # list of VMs with working balloon river
+ my $idlist1 = []; # list of VMs with memory pressure
+ my $idlist2 = []; # list of VMs with enough free memory
+
+ foreach my $vmid (keys %$vmstatus) {
+ my $d = $vmstatus->{$vmid};
+ next if !$d->{balloon}; # skip if balloon driver not running
+ next if !$d->{balloon_min}; # skip if balloon value not set in config
+
+ push @$idlist, $vmid;
+
+ if (($goal > 0) && $d->{freemem} &&
+ ($d->{freemem} > $d->{maxmem}*0.25) &&
+ ($d->{balloon} >= $d->{balloon_min})) {
+ push @$idlist2, $vmid;
+ &$log("idlist2 $vmid $d->{balloon}, $d->{balloon_min}, $d->{freemem}\n");
+ } else {
+ push @$idlist1, $vmid;
+ &$log("idlist1 $vmid $d->{balloon}, $d->{balloon_min}, $d->{freemem}\n");
+ }
+ }
+
+ my $res = {};
+
+ if ($goal > 10*1024*1024) {
+ &$log("grow request $goal\n");
+ # we priorize VMs with memory pressure
+ if (!&$grow_func($res, $idlist1, $goal)) {
+ &$grow_func($res, $idlist2, $goal);
+ }
+ } elsif ($goal < -10*1024*1024) {
+ &$log("shrink request $goal\n");
+ my ($shares_total, undef, $alloc_old) = &$get_summary($idlist);
+ my $alloc_new = $alloc_old + $goal;
+ $alloc_new = 0 if $alloc_new < 0;
+ &$log("shares_total: $shares_total $alloc_new\n");
+
+ foreach my $vmid (@$idlist) {
+ my $d = $vmstatus->{$vmid};
+ my $shares = $d->{shares} || 1000;
+ next if $shares < 0; # just to be sure
+ my $min = $d->{balloon_min} + int(($alloc_new/$shares_total)*$shares);
+ my $new = $d->{balloon} - $maxchange;
+ $res->{$vmid} = $new > $min ? $new : $min;
+ }
+ } else {
+ &$log("do nothing\n");
+ # do nothing - requested change to small
+ }
+
+ foreach my $vmid (@$idlist) {
+ next if !$res->{$vmid};
+ my $d = $vmstatus->{$vmid};
+ my $diff = int($res->{$vmid} - $d->{balloon});
+ my $absdiff = $diff < 0 ? -$diff : $diff;
+ if ($absdiff > 0) {
+ &$log("BALLOON $vmid to $res->{$vmid} ($diff)\n");
+ eval {
+ PVE::QemuServer::vm_mon_cmd($vmid, "balloon",
+ value => int($res->{$vmid}));
+ };
+ warn $@ if $@;
+ }
+ }
+}
+
sub update_qemu_status {
my $ctime = time();
my $vmstatus = PVE::QemuServer::vmstatus(undef, 1);
+ eval { auto_balloning($vmstatus); };
+ syslog('err', "auto ballooning error: $@") if $@;
+
foreach my $vmid (keys %$vmstatus) {
my $d = $vmstatus->{$vmid};
my $data;