#!/usr/bin/perl -w

# mail gateway performance test skript
# this skript simply send 600 testmail to the specified server
#
# proxmox openvz installation: http://www.proxmox.com/forum/showthread.php?t=221
# 0.) upload an license (its free)
#     http://www.proxmox.com/cms_proxmox/en/products/mail-gateway/download/download-license-free.html
# 1.) upload the ssh key to the test server /root/.ssh/authorized_keys 
# 2.) modify the Rule system to simply Block the mails (add action 
#     object 'Block' to rule 'Mark Spam'
# 3.) disable network tests (use the web inteface)
#     Configuration/Mail Proxy/Options/Use Greylisting
#     Configuration/Mail Proxy/Options/Use SPF
#     Configuration/Spam Detector/Options/Use Razor Network
#     Configuration/Spam Detector/Options/Use RBL checks
#
# 4.) send a single testmail: 
#     ./perftest.pl -m SERVER_IP -r test@yourdomain.com -t
#     (use tail -100f /var/log/syslog to see what happens on the mail gateway)
# 5.) send 600 testmails: 
#     ./perftest.pl -m SERVER_IP -r test@yourdomain.com  
# 6.) Results: MMPD: $mmpd ($mmpd_ppek) MPS: $mps
#     $mmpd = nillion mails per day, 
#     $mmpd_peek = million mails per day (peek rate), 
#     $mps = mails per second

use strict;
use Carp;
use Getopt::Long;

use Proxmox::SMTP;
use MIME::Entity;
use Net::SMTP;
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
use POSIX ":sys_wait_h";


my $sender ='sender@example.tld'; 
my $domain = 'example.tld';
my $target = 'test@example.tld';

my $opt_ip;
my $opt_port;
my $opt_rcpt;
my $opt_test;
my $opt_processcount; 
my $opt_loadtype;

my $loadtypes = {
    gtube => 1,
};

sub usage {
    print STDERR "available options:\n\n";

    print STDERR "\t-m --mailserver IPADDRESS\tIP address of server to test\n";
    print STDERR "\t-r --rcpt EMAIL\t\t\tsend testmails to EMAIL\n";
    print STDERR "\t-t --test\t\t\tsend a single testmail\n";
    print STDERR "\t-p --processcount INT\t\tuse INT paralell connections\n";
    print STDERR "\t-l --loadtype [gtube]\t\tspecify mail content\n";

    print STDERR "\n";
    exit (-1);
} 

if (!GetOptions ('mailserver=s' => \$opt_ip, 
		 'rcpt=s' => \$opt_rcpt, 
		 'test' => \$opt_test,
		 'loadtype=s' => \$opt_loadtype,
		 'processcount=s' => \$opt_processcount)) {

    usage();
}

$opt_ip = '127.0.0.1' if !$opt_ip;
$opt_port = 25 if !$opt_port;
$opt_rcpt = $target if !$opt_rcpt;
$opt_processcount = 20 if !$opt_processcount; # run X parallel processes
$opt_loadtype = 'gtube' if !$opt_loadtype;

if ($opt_loadtype) {
    usage () if !exists ($loadtypes->{$opt_loadtype});
}

sub run_workers {
    my ($processcount, $count, $starttime) = @_;

    $starttime = [gettimeofday] if !$starttime;

    foreach my $p (1 .. $processcount) {
	if (my $pid=fork) {
	    #print "Process $p started!\n";    
        } else {                 
	    for (my $i=0; $i < $count; $i++) {
		my $id = "ID".$p."_". $i;
		my $long_id = $id."_r";
		#print "\nproc: $p : sending... (From: $sender, ID: $long_id)";
		if (send_simple_testmail($sender, $long_id, $opt_loadtype)) {                        
		    print OUT $long_id."\n";  
		    #print "- ok\n"; 
		} else {
		    warn "failure";
		    #print "- not ok\n";
		}
	    }              
            exit (0);          
        }
    }

    # wait for children
    1 while (wait > 0);

    my $elapsed = int(tv_interval ($starttime) * 1000);

    my $mcount = $processcount * $count;

    my $rate = ($processcount * $count *1000 ) / $elapsed;

    return ($rate, $mcount);
}


sub send_simple_testmail {
    my ($sender, $id, $type) = @_;
    
    my $top = MIME::Entity->build(From    => $sender,
				  To      => $opt_rcpt,
				  Subject => "Proxmox Test Message: $id",
				  Data    => "This is just a smal test message\n" .
"XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X");

    if ($type eq 'gtube') {
	# do nothing
    } else {
	die "unknown type '$type'";
    } 

    my $smtp = Net::SMTP->new ($opt_ip, Port => $opt_port);
    
    if (!$smtp) {
	warn "unable to connect $!";
	return 0;
    }

    $smtp->mail ($sender);
    if ($smtp->to ($opt_rcpt)) {
        
        $smtp->data();
        $top->print ($smtp);
        $smtp->datasend ("\n");
        if (!$smtp->dataend ()) {
	    warn "sending data failed";
	    return 0;
	}
	return 1;
    } else {
        return 0;   
    }
}
 
sub getload {

    my $res = `ssh ${opt_ip} "mailq|grep '^--'"`;

    if ($res =~ m/^--.*in\s+(\d+)\s+Requests?\.$/) {
	return $1;
    }

    return 0;
};

if ($opt_test) {
    
    print "Sending test mail\n";
    print "Current Load: " . getload . "\n";

    print "Sending Testmail ...";
    send_simple_testmail ($sender, "testmail", $opt_loadtype);
    print "  done\n";

    print "Current Load: " . getload . "\n";
} else {
    
    my $minmail = int (600/$opt_processcount);
    my $maxmails = $minmail > 30 ? $minmail : 30;
    my $mcount = $opt_processcount * $maxmails;

    print "STARTING SERVER Performance test (sending $mcount mails)\n";

    my $starttime = [gettimeofday];

    my $lastload = getload ();

    die "load is > 0 ($lastload)" if $lastload;

    my ($mps, $count) = run_workers ($opt_processcount, $maxmails, $starttime);

    my $peek = $mps;

    my $load = getload ();
    
    print "SENT MAILS: $mps $load\n";
    
    while ($load > 100) {
	print "LOAD $load - wating...\n";
	sleep 1;
	$load = getload ();
    }

    print "LOAD $load\n";

    my $elapsed = int(tv_interval ($starttime) * 1000);
    $mps = (($mcount-$load) *1000 ) / $elapsed;
    my $mmpd = ($mps*3600*24)/1000000.0;
    my $peekmmpd = ($peek*3600*24)/1000000.0;
    printf ("DONE: MMPD: %.2f (%.2f) MPS: %.2f\n", $mmpd, $peekmmpd, $mps);

}

exit 0;
