#!/usr/bin/env perl

use threads;
use threads::shared;

# Forces a buffer flush after every print
$| = 1;

use warnings;
use strict;

use Sys::Virt;
use Sys::Virt::Domain;

use Switch;

#root snmp branch
my $oid_root = ".1.3.6.1.4.1.40442";
my $libvirt_uri = 'xen:///';

#lots of shared vars
my $debug :shared;
$debug = 0;

my @vm_info :shared;
$vm_info[0] = 0;

#start update threads
my $thr_update_vm_info = threads->create(\&update_vm_info, 30);

#
#main thread
#
while (<STDIN>){
	my $cmd;
	my $req;

	#make PING-PONG fast
	if (m/^PING/){
		print "PONG\n";
		next;
	}

	#shutdown
	if(m/^$/) {
		last;
	}
	
	#get request
	chomp($cmd = $_ );
	chomp($req = <STDIN> );

	$debug && print "Got $cmd command\n";

	switch($cmd){
		case /get/i {
			$debug && print "Processing GET command with request $req\n";
			print &get($req), "\n";
		}
		case /getnext/i {
			$debug && print "Processing GETNEXT command with request $req\n";
			print &getnext($req), "\n";
		}
		case /set/i {
			$debug && print "Processing SET command with request $req\n";
			&set($req);
		}
		else {
			$debug && print "No such command like $cmd\n!";
			print "NONE\n";
			next;
		}
	}
}

threads->exit();
exit 0;

#get request handling
sub get {
	my $oid = shift;

	if ($oid =~ /^$oid_root.([0-9]+).*$/){
		switch($1) {
			case 9 {
				return &force_update_vm_info($oid);
			}
			case 10 {
				return &get_vm_oids($oid);
			}
		}
	}

	return "NONE";
}

#getnext request handling
sub getnext {
	my $oid = shift;

	if ($oid =~ /^$oid_root.([0-9]+).([0-9]+).([0-9]+)$/){
		&get(sprintf("$oid_root.$1.$2.%d", $3 + 1));
	}
	return "NONE";
}

#set request handling
sub set {
	return "NONE\n";
}

#
#get handlers
#
sub get_vm_oids {
	my $oid = shift;

	if ($oid =~ /^$oid_root.10.([0-9]+)$/) {
		if ($1 == 0 and defined $vm_info[$1]) {
			return "$oid\nstring\n$vm_info[$1]";
		}
	} elsif ($oid =~ /^$oid_root.10.([0-9]+).([0-9]+)$/) {
		my $val = &get_elem_from_string_arr($vm_info[$1], $2);
		chomp($val);
		return "$oid\nstring\n$val" if defined $val;
	}

	return "NONE";
}

sub collect_vm_info() {
	my $libvirt_conn = shift;
	my @vm_info = (0);
	
	#use XML::Parser
	$ENV{XML_SIMPLE_PREFERRED_PARSER} = 'XML::Parser';

	if ($libvirt_conn->num_of_domains() > 0){
		#get all domains
		my @domains = $libvirt_conn->list_domains();
	
		foreach my $d (@domains) {
			
			my $dom_name = $d->get_name;
			
			if ( $dom_name =~ m/Domain/i || $dom_name =~ m/STORAGENODE/i ){
                                next;
                        };

			my $dom_id = $d->get_id;
			my $dom_vnc_port = `xenstore-read /local/domain/$dom_id/console/vnc-port`;
			chomp($dom_vnc_port);

			push(@vm_info, join('::', $dom_id, $dom_name, $dom_vnc_port));
		}
	}

	$vm_info[0] = $#vm_info;
	return @vm_info;
}

sub force_update_vm_info {
	my $oid = shift;

	my $libvirt_conn = &get_libvirt_conn($libvirt_uri);
	@vm_info = &collect_vm_info($libvirt_conn);

	return "$oid\nstring\nOID Tree Updated";
}

sub update_vm_info {
	my $update_interval = shift;
	my $start_time;

	my $libvirt_conn = &get_libvirt_conn($libvirt_uri);

	while(1){
           	$start_time = time();
		@vm_info = &collect_vm_info($libvirt_conn);
		sleep &get_delay_time($start_time, $update_interval);
	}
}

sub get_libvirt_conn() {
	my $uri = shift;
	my $conn;
	
	eval {
		$conn = Sys::Virt->new(uri => $uri);
	};

	if ($@){
		die "Unable to open connection to $uri\n";
	}

	return $conn;
}

sub get_delay_time {
	my $start_time = shift;
	my $update_interval = shift;
	my $delay_time = 0;

	$delay_time = $update_interval - (time() - $start_time);
	if ($delay_time < 0) {
		$delay_time = 0.5;
	}

	return $delay_time;
}

sub get_elem_from_string_arr {
	my $arr = shift;
	my $el_num = shift;
	my @a;

	if(defined $arr){
		@a = split('::', $arr);

		if (exists $a[$el_num]) {
			return $a[$el_num];
		}
	}

	return undef;
}

__END__
