#!/usr/bin/perl -w

use lib qw(../../);
use strict;
use Storable qw(dclone);
use Data::Dumper;
use PVE::AutoBalloon;

my $debug = 0;

my $test_status1 = {
    100 => {
	maxmem => GB(2),
	shares => 2000,
	balloon => GB(1),
	balloon_min => GB(1),
	freemem => MB(0),
    },
    101 => {
	maxmem => GB(2),
	shares => 1000,
	balloon => GB(1),
	balloon_min => GB(1),
	freemem => MB(0),
    },
};

abtest($test_status1, 0);
abtest($test_status1, MB(90), 100 => MB(1060), 101 => MB(1030));
abtest($test_status1, MB(150), 100 => MB(1100), 101 => MB(1050));
abtest($test_status1, MB(270), 100 => MB(1100), 101 => MB(1090));
absim($test_status1, MB(180), 100 => MB(1120), 101 => MB(1060));
absim($test_status1, MB(270), 100 => MB(1180), 101 => MB(1090));
absim($test_status1, MB(600), 100 => MB(1300), 101 => MB(1300));
absim($test_status1, MB(900), 100 => MB(1600), 101 => MB(1300));

my $test_status2 = {
    100 => {
	maxmem => GB(2),
	shares => 2000,
	balloon => GB(2),
	balloon_min => GB(2),
	freemem => MB(0),
    },
    101 => {
	maxmem => GB(2),
	shares => 1000,
	balloon => GB(1),
	balloon_min => GB(1),
	freemem => MB(0),
    },
};

abtest($test_status2, 0);
abtest($test_status2, MB(18), 101 => MB(1018));
abtest($test_status2, MB(500), 101 => MB(1100));

my $test_status3 = {
    100 => {
	maxmem => GB(2),
	shares => 2000,
	balloon => GB(2),
	balloon_min => GB(2),
	freemem => MB(0),
    },
    101 => {
	maxmem => GB(2),
	shares => 1000,
	balloon => GB(1)+MB(7),
	balloon_min => GB(1),
	freemem => MB(0),
    },
    102 => {
	maxmem => GB(2),
	shares => 1000,
	balloon => GB(1),
	balloon_min => GB(1),
	freemem => MB(512),
    },
};

abtest($test_status3, 0);
abtest($test_status3, MB(11), 101 =>  MB(1018));
abtest($test_status3, MB(80), 101 =>  MB(1087));
abtest($test_status3, MB(200), 101 =>  MB(1107));

my $status = absim($test_status3, MB(593), 101 =>  MB(1300), 102 =>  MB(1300));
absim($status, -MB(200), 101 => MB(1200), 102 => MB(1200));
absim($status, -MB(400), 101 => MB(1200), 102 => GB(1));
absim($status, -MB(593), 101 => MB(1007), 102 => GB(1));
exit (0);

sub abapply {
    my ($vmstatus, $res, $sum) = @_;

    my $changes = 0;
    my $abschanges = 0;
    foreach my $vmid (keys %$res) {
	my $diff = $res->{$vmid} - $vmstatus->{$vmid}->{balloon};
	if ($diff != 0) {
	    # fixme: adjust freemem ?
	    $vmstatus->{$vmid}->{freemem} += $diff;
	    $vmstatus->{$vmid}->{freemem} = 0 if $vmstatus->{$vmid}->{freemem} < 0;
	    $vmstatus->{$vmid}->{balloon} = $res->{$vmid};
	    $sum->{$vmid} = $res->{$vmid};
	    $changes += $diff;
	    $abschanges += $diff > 0 ? $diff : -$diff;
	}
    }

    return ($changes, $abschanges);
}

my $tcount = 0;
sub absim {
    my ($vmstatus, $goal, %expect) = @_;

    $tcount++;

    print "BALLOON SIM $tcount\n" if $debug;
    
    $vmstatus = dclone($vmstatus); # do not change original

    my $changes = 0;
    my $abschanges = 0;
    my $sum = {};
    do {
	my $res = PVE::AutoBalloon::compute_alg1($vmstatus, $goal, MB(100), $debug);
	print Dumper($res) if $debug;
	($changes, $abschanges) = abapply($vmstatus, $res, $sum);
	$goal -= $changes;
    } while ($abschanges);

    abcheck($sum, %expect);

    print "BALLOON SIM END\n" if $debug;
    print Dumper($vmstatus) if $debug;

    return $vmstatus;
}

sub abcheck {
    my ($res, %expect) = @_;

    foreach my $vmid (keys %expect) {
	my $ev = $expect{$vmid};
	if (defined ($res->{$vmid})) {
	    die "T$tcount: wrong value for VM $vmid ($ev != $res->{$vmid})\n"
		if $ev != $res->{$vmid};
	} else {
	    die "T$tcount: missing value for VM $vmid (extected $ev)\n";
	}
    }

    foreach my $vmid (keys %$res) {
	die "T$tcount: got unexpected result for $vmid\n"
	    if (defined($res->{$vmid}) && 
		!defined($expect{$vmid}));
    }
}

sub abtest {
    my ($vmstatus, $goal, %expect) = @_;

    $tcount++;

    print "BALLOON TEST $tcount\n" if $debug;
    my $res = PVE::AutoBalloon::compute_alg1($vmstatus, $goal, MB(100), $debug);
    print Dumper($res) if $debug;

    abcheck($res, %expect);

    print "\n\n" if $debug;

    return $res;
}

sub MB {
    my $mb = shift;
    return $mb*1000*1000;
};
sub GB {
    my $gb = shift;
    return $gb*1000*1000*1000;
};
