#!/usr/bin/perl

use lib "/etc/kvm/api";
#push(@INC, "/etc/kvm/api");

use strict;
use Getopt::Std;
use POSIX qw(floor);

use KVMapi;

my $command = shift;
my $vm_name = shift;
my $maxwait = 60;

my $isEnabled = (-d "/sys/devices/system/kvm");

if ($command eq "shutdown" || $command eq "stop")
{
	unless ($isEnabled)
	{
		printf("KVMctrl: host is not kvm enabled...\n");
		exit(1);
	}

	my $kvm = KVMapi->new($vm_name);
	unless ($kvm->isRunning())
	{
		printf("KVMctrl: machine $vm_name is not running...\n");
		exit(1);
	}
	$kvm->sendShutdown();	
	printf("KVMctrl: shutdown request sent to $vm_name...\n");
}
elsif ($command eq "reset")
{
	unless ($isEnabled)
	{
		printf("KVMctrl: host is not kvm enabled...\n");
		exit(1);
	}

	my $kvm = KVMapi->new($vm_name);
	unless ($kvm->isRunning())
	{
		printf("KVMctrl: machine $vm_name is not running...\n");
		exit(1);
	}
	$kvm->sendReset();
	printf("KVMctrl: hardware reset sent to $vm_name...\n");
}
elsif ($command eq "destroy")
{
	unless ($isEnabled)
	{
		printf("KVMctrl: host is not kvm enabled...\n");
		exit(1);
	}

	my $kvm = KVMapi->new($vm_name);
	unless ($kvm->isRunning())
	{
		printf("KVMctrl: machine $vm_name is not running...\n");
		exit(1);
	}
	$kvm->destroy();
	my %options = $kvm->getOptions();
	my $pidfile = $options{"pidfile"};
	if (-f $pidfile)
	{
		`kill \$( cat $pidfile ) 2>/dev/null`;
		unlink($pidfile);
	}

	printf("KVMctrl: machine $vm_name destroyed...\n");
}
elsif ($command eq "isrunning")
{
	unless ($isEnabled)
	{
		printf("KVMctrl: host is not kvm enabled...\n");
		exit(1);
	}
	my $kvm = KVMapi->new($vm_name);
	if ($kvm->isRunning())
	{
		exit(0);
	}
	exit(1);
}
elsif ($command eq "reboot")
{
	my $cnt = 0;
	my $kvm = KVMapi->new($vm_name);
	if ($kvm->isRunning())
	{
		printf("KVMctrl: sending shutdown command and waiting for completition (maxwait is $maxwait seconds)\n");
		system("/usr/sbin/kvmctrl shutdown $vm_name");
		while ($cnt < $maxwait)
		{
			sleep(1);
			$kvm = KVMapi->new($vm_name);
			$cnt++;
			next if ($kvm->isRunning());
	
			printf("KVMctrl: machine stopped...\n");
			$cnt = 0;
			last;
		}
	}
	if ($cnt == 0)
	{
		printf("KVMctrl: starting machine...\n");
		system("/usr/sbin/kvmctrl start $vm_name");
	}
	else
	{
		printf("KVMctrl: forcing a reset of machine...\n");
		system("/usr/sbin/kvmctrl reset $vm_name");
	}
	sleep(1);
	my $rval = system("/usr/sbin/kvmctrl isrunning $vm_name");
	if ($rval != 0)
	{
		printf("KVMctrl: for some reason the vm didn't boot... retrying once...\n");
		system("/usr/sbin/kvmctrl start $vm_name");
	}
}
elsif ($command eq "console")
{
	unless ($isEnabled)
	{
		printf("KVMctrl: host is not kvm enabled...\n");
		exit(1);
	}

	my $kvm = KVMapi->new($vm_name);
	unless ($kvm->isRunning())
	{
		printf("KVMctrl: machine $vm_name is not running...\n");
		exit(1);
	}

	my %options = $kvm->getOptions();
	system("nc localhost " . $options{"console_port"});
#	printf("automatic console attachment not yet implemented... need to execute the following...");
#	$kvm->attachConsole();
}
elsif ($command eq "types")
{
	printf("vga:  std,cirrus,vmware,none\n");
	printf("disk: virtio,ide,scsi\n");
	printf("vif:  virtio,rtl1389,e1000\n");
}
elsif ($command eq "start")
{
	unless ($isEnabled)
	{
		printf("KVMctrl: host is not kvm enabled...\n");
		exit(1);
	}

	my $kvm = KVMapi->new($vm_name);
	if ($kvm->isRunning())
	{
		printf("KVMctrl: machine $vm_name is already running...\n");
		exit(1);
	}
	my %options = $kvm->getOptions();
	my $args = "";
	if ($options{"boot"} && $options{"boot"} ne 'c')
	{
		$args .= sprintf("-boot %s \\\n", $options{"boot"});
	}
	$args .= sprintf("-name %s \\\n", $options{"name"});
	$args .= sprintf("-smp %s \\\n", $options{"cpus"});
	$args .= sprintf("-m %s \\\n", $options{"mem"});
#	$args .= "-localtime -daemonize -usb -usbdevice tablet \\\n";
	$args .= "-daemonize -usb -usbdevice tablet \\\n";
	$args .= "-k de -parallel none \\\n";
	$args .= sprintf("-vga %s \\\n", $options{"vga"});
	$args .= sprintf("-pidfile %s \\\n", $options{"pidfile"});
	
	if ($options{"vnc_enabled"})
	{
		$args .= sprintf("-vnc %s:%s", $options{"vnc_listen"}, $options{"id"});
		if ($options{"vnc_password"} ne "")
		{
			$args .= ",password";
		}
		$args .= " \\\n";
	}
	if ($options{"localtime"})
	{
		$args .= sprintf("-localtime \\\n");
	}

	for (my $i = 0; $i < $options{"disks"}; $i++)
	{
		my $key = "disk" . $i;
		$args .= "-drive index=" . $i;
		#disk: type=virtio, media=disk, phy=/dev/vg/europdf-vol0
		$args .= sprintf(",media=%s", $options{$key . "_media"});
		$args .= sprintf(",if=%s", $options{$key . "_type"});
		$args .= sprintf(",file=%s", $options{$key . "_phy"});

		if ($options{"boot"} eq "c" && $i == 0)
		{
			$args .= sprintf(",boot=on");
		}
		$args .= " \\\n";
	}

	for (my $i = 0; $i < $options{"nics"}; $i++)
	{
		my $key = "nic" . $i;
		my $vlan = $options{$key ."_vlan"};
		$args .= "-net nic";
		#vif: type=virtio, bridge=br0, mode=route, ip=78.47.10.42
		if ($options{"boot"} eq 'n')
		{
			$args .= ",model=e1000";
		}
		else
		{
			$args .= sprintf(",model=%s", $options{$key . "_type"});
		}

		if ($options{$key . "_mac"} ne "")
		{
			$args .= sprintf(",macaddr=%s", $options{$key . "_mac"});
		}
		$args .= sprintf(",vlan=%s", $vlan);
		$args .= " -net tap";
		# $args .= sprintf(",ifname=vif_%s", $options{"name"});
		$args .= sprintf(",ifname=vif_%s_%s", $options{"id"}, $vlan);
		$args .= sprintf(",vlan=%s", $vlan);
		$args .= ",script=/etc/kvm/api/ifup";
		$args .= ",downscript=/etc/kvm/api/ifdown";
		$args .= " \\\n";
	}


	$args .= sprintf("-monitor tcp:127.0.0.1:%s,server,nowait \\\n", $options{"console_port"});
#	if ($options{"serial_port"})
#	{
#		$args .= sprintf("-serial tcp:127.0.0.1:%s,server,nowait \\\n", $options{"serial_port"});
#	}
	if ($options{"kernel"})
	{
		$args .= sprintf("-kernel %s \\\n", $options{"kernel"})
	}
	if ($options{"initrd"})
	{
		$args .= sprintf("-initrd %s \\\n", $options{"initrd"})
	}
	if ($options{"kernel-append"})
	{
		$args .= sprintf("-append \"%s\" \\\n", $options{"kernel-append"});
	}
	$args .= "-tdf \n";


	print "args: '$args'\n\n";
#	exit(0);
	my $rval = system("/usr/bin/kvm $args");
	if ($rval ne 0)
	{
		printf("kvm %s couldn't be started, code: %s\n", $options{"name"}, $rval);
		exit(1);
	}	

	printf("kvm %s started...\n", $options{"name"});

	if ($options{"vnc_password"} ne "")
	{
		$kvm->sendVncPassword($options{"vnc_password"});
	}
}
elsif ($command eq "list")
{
	opendir(DIR, "/etc/kvm");
	my @files = readdir(DIR);
	closedir(DIR);

	my $syntax = "%-25s%3s%6s%6s%7s%2s%-15s\n";
	printf($syntax, "Name", "ID", " Mem", " VCPUs", " PID", "", "State");
	my $usedRam;
	my $neededRam;
	foreach my $file (@files)
	{
		next unless ($file =~ /\.kvm$/);
		my $name = $file;
		$name =~ s/\.kvm//g;
#		printf ("file: '%s'\n", $name);
		my $kvm = KVMapi->new($name);
		my %options = $kvm->getOptions();
		$neededRam += $options{"mem"};
		if ($kvm->isRunning())
		{
			$usedRam += $options{"mem"};
		}

		printf($syntax,
			$name, 
			$options{"id"}, 
			$options{"mem"}, 
			$options{"cpus"}, 
			$options{"pid"},
			"", $options{"state"});
	}	

	my $cpus=`grep -w processor /proc/cpuinfo | wc -l`;
	chomp($cpus);
	
	my $memory=`grep -w MemTotal /proc/meminfo | awk '{ print \$2 }'`;
	chomp($memory);
	$memory = floor($memory / 1024);
	$memory -= $neededRam;

	my $state = "kvm disabled";
	if ($isEnabled)
	{
		$state = "kvm enabled";
	}
	printf($syntax, "domain-0", "", $memory, $cpus, "", "", $state);
}
else
{
	printf("Usage: kvmctrl <subcommand> [args]\n");
	printf("\n");
	printf("Control, list, and manipulte KVM guest instances.\n");
	printf("\n");
	printf("Common 'kvmctrl' commands:\n");
	printf("\n");

	printf(" console                Attach to <Domain>'s kvm console.\n");
	printf(" list			List information about all/some domains.\n");
	printf(" help			Display this message.");
	printf(" start                  Create a domain based on <Domain>.\n");
	printf(" shutdown	        Issue a system shutdown command on <Domain>.\n");
	printf(" reset                  Reset a domain immediately.\n");
	printf(" reboot			Issues a shutdown, waits for completition and recreates <Domain>.\n");
	printf(" destroy                Terminate a domain immediately.\n");
	printf("\n");
	printf("<Domain> must be the Domain Name.\n");
}


