#!/usr/bin/perl
use strict;

# Constants
my $MACHINE_NAME_LEN = 20;
my $LOCATION_LEN = 60;
my $ARCH_LEN = 60;
my $TYPE_LEN = 40;
my $CPUTYPE_LEN = 80;
my $NETNAME_LEN = 15;
my $IF_LEN = 15;
my $DISTRO_LEN = 80;
my $DISTRO_ROOT_LEN = 30;
my @DISTRO_LIST = ("/etc/redhat-release", "/etc/SuSE-release",
		   "/etc/debian_version");
my $IP_REGEX = '(\d{1,3}\.){3}\d{1,3}';

# Gathered from /proc/cpu_info
my $num_cpus = 0;
my $cpu_type = "";
my $arch = "";
my $speed = 0;
my $cache = 0;
my $machine_type = "";
my $ht_possible = 0;

# Gathered from user
my $machine_name;
my $c_port;
my $m_port;
my $ded = "y";
my $state = "a";
my $reboot_delay;

# Gathered from elsewhere
my $ht_mult = 0;
my @distros;	# ($distro_name, $distro_root)*
my $model_num = 0;
my $memory = 0;
my $free_partitions = "";
my $free_pars = 0;
my $free_disk_list = "";
my $free_disks = 0;
my $primary_ip = "";
my $primary_if = "";
my $primary_netname = "primary";
my @ip_list = ();
my @if_list = ();
my @netname_list = ();
my @eth_info = ();

my $value = "";
my $default = "";

my $no_questions = 0;

my $server_repo;

foreach my $opt (@ARGV) {
	if ($opt =~ /help/) {
		print "machineinfo - Gathers basic machine information for GMM.\n";
		print "usage:\n";
		print "  machineinfo [OPTIONS] [DATS SERVER REPO]\n\n";
		print "  --no_questions   Refrain from asking questions.\n";
		print "  --help           This help screen.\n";
		exit 1;
	}
	elsif ($opt =~ /no_questions/) { $no_questions = 1; }
	else { $server_repo = $opt; }
}

open(DATS_CONFIG, "< /etc/autobench.conf") || die "Please edit /etc/autobench.conf first\n";
close(DATS_CONFIG);

if (open(MCH_INFO, "< machine.info")) { #Default values may exist
	while(<MCH_INFO>) {
		chomp;
		$value = (split /=/)[1];
		if (/^machine_name=/) { $machine_name = $value; }
		if (/^console_port=/) { $c_port = $value; }
		if (/^mgmt_port=/) { $m_port = $value; }
		if (/^dedicated=/) { $ded = $value; }
		if (/^state=/) { $state = $value; }
		if (/^reboot_delay=/) { $reboot_delay = (split / minutes/, $value)[0]; }
	}
	close MCH_INFO;
}

# Quick probe to see what distro we're running.
foreach my $filename (@DISTRO_LIST) {
	if (stat $filename) { 
		my $name = (split /\n/, `cat $filename`)[0];
		my $root = (split /\n/, `df $filename`)[1];
		$root = (split /\s+/, $root)[0];
		if ($filename =~ /debian/) { $name = "Debian $name"; }
		my @pair = ($name, $root);
		push @distros, \@pair;
	}
}

my $keep_looping = $no_questions ? 0 : 1;

# Get some possibly default information for the machine name.
unless ($machine_name) {
	$machine_name = `hostname -s 2>/dev/null`;
	chomp $machine_name;
	if ($? >> 8) {
		$machine_name = `uname -n 2>/dev/null`;
		chomp $machine_name;
		$machine_name =~ s/\..*//;
		if (length $machine_name >= $MACHINE_NAME_LEN) {
			$machine_name = substr $machine_name, 0, $MACHINE_NAME_LEN-1;
		}
	}
}

$default = $machine_name ? "($machine_name) " : "";
while ($keep_looping) {
	print "Machine nickname (max $MACHINE_NAME_LEN chars.): $default";
	$value = <STDIN>;
	chomp $value;
	$keep_looping = 0;
	if (length $value > $MACHINE_NAME_LEN) {
		print "Machine name must be $MACHINE_NAME_LEN characters or less.\n";
		$keep_looping = 1;
	}
}
$machine_name = $value ? $value : $machine_name;

$keep_looping = $no_questions ? 0 : 1;
$default = $reboot_delay ? "($reboot_delay) " : "";
print "The reboot delay is used to determine how long the DATS system should\n";
print "wait after this machine has rebooted before considering the reboot\n";
print "to have failed.\n";
while ($keep_looping) {
	print "Reboot delay in minutes (default 20): $default";
	$value = <STDIN>;
	chomp $value;
	if (length($value) == 0) {
		$value = 20;
	}
	$keep_looping = 0;
	if ($value and $value =~ /[^\d:]/) {
		print "Please use only digits for the interval.\n";
		$keep_looping = 1;
	}
}
$reboot_delay = $value ? $value : $reboot_delay;
$reboot_delay = "$reboot_delay minutes";

$value=qx'cat /etc/autobench.conf | grep "^[^#]\?DATS_CONFIG_DEDICATED"';
$value = (split /=/,$value)[1];

if ( not ( $value =~ /^n(o)?$/ or $value =~ /^y(es)?$/ ) ) {
	if (not $no_questions) {
		$default = ($ded and ($ded eq "n")) ? "(y/N)" : "(Y/n)";
		print "Dedicated? $default ";
		$value = <STDIN>;
		chomp $value ;
		if (!$value) { 
			$value = ($ded and ($ded eq "n")) ? "n" : "y";
		}
	}
}
$ded =  (lc($value) =~ /^n(o)?$/) ? "n" : "y";

if ($ded eq "n" and not $no_questions) {
	$default = $state ? "($state) " : "(a)";
	print "(a)vailable, (b)usy\n";
	print "Current state: $default";
	$value = <STDIN>;
	chomp $value;
	$state = $value ? $value : $state;
}

# Grab some info from /proc/cpuinf
# TODO Round speed to a more likely number such as:
# 333, 450, 500, 550, 667, 700, 800, 850, 866, 900, 2k, etc.

open (CPU_INFO, "< /proc/cpuinfo");
while (<CPU_INFO>) {
	chomp;
	$value = (split /: /)[1];
	if (/^processor/) { $num_cpus++; }
	if (/^model name|^(cpu)(\s)*:|^family\s*:/) { $cpu_type=$value; }
	if (/^vendor id/) { $cpu_type = $value; }
	if (/^cpu MHz|^clock|^bogomips per cpu/) { $speed = int $value; }
	if (/^cache size/) { $cache=int substr $value, 0, -2; }
	if (/^machine/) { $machine_type=$value; }
	if (/^flags\s*:/) { $ht_possible = $value =~ / ht / ? 1 : 0; }
}
close CPU_INFO;

if (length $machine_type > $TYPE_LEN) { 
	$machine_type = substr $machine_type, 1, $TYPE_LEN;
}

if (length $cpu_type > $CPUTYPE_LEN) { 
	$cpu_type = substr $cpu_type, 1, $CPUTYPE_LEN;
}

# If we are on a processor capable of hyperthreading, and if the
# cpucounter4linux program is available or obtainable, let's
# run that program and determine whether ht is on.

my $ht_res;

if ($ht_possible) {
	$ht_res = `./cpucounter4linux -v`;
}

# If not installed, let's install OD.
if ($ht_possible and not $ht_res and $server_repo) {
	# Copy tarball from location
	`wget -q $server_repo/utilities/cpucounter.tar.gz`;
	(not ($? >> 8)) and `tar xzf cpucounter.tar.gz`;
	(not ($? >> 8)) and `pushd 0.9.6 ; make ; popd`;
	(not ($? >> 8)) and $ht_res = `./0.9.6/cpucounter4linux -v`;
	`rm -rf 0.9.6/ ; rm -f cpucounter.tar.gz`;
}

if ($ht_possible and not $ht_res) {
	print "Unable to determine whether hyper-threading is enabled.\n";
} elsif ($ht_possible) { # Extract appropriate information.
	my $enabled = 0;
	my $capable = 0;
	my $mult = 0;
	my $ht_num_cpus = 0;
	foreach (split /\n/, $ht_res) {
		# The counter code has a typo spelling "capabable" instead
		# of "capable".
		if (/^Hyper-Threading cap(ab)?able processor/) {
			$capable = /: Capable$/ ? 1 : 0;
		}
		if (/^Hyper-Threading technology/) { 
			$enabled = /: Enabled$/ ? 1 : 0;
		}
		if (/^Logical processors per physical/) {
			$mult = (split / : /)[1];
		}
		if (/^Number of physical processors/) {
			$ht_num_cpus = (split / : /)[1];
		}
	}

	if ($capable) {
		$num_cpus = $ht_num_cpus;
		$ht_mult = $mult;
	}
}

# An alternate way of determining architecture was presented as:
# uname -m
# Here is some code to do that:
my $arch = qx'uname -m';
chomp $arch;
# Substitutions match those used in Linux kernel makefile.
$arch =~ s/i.86/x86/;
$arch =~ s/sun4u/sparc64/;
$arch =~ s/arm./arm/;
$arch =~ s/sa110/arm/;

if (length $arch > $ARCH_LEN) { $arch = substr $arch, 1, $ARCH_LEN; }

$free_partitions=qx'cat /etc/autobench.conf | grep "^[^#]\?DATS_FREE_PARTITIONS"';
chomp $free_partitions;
$free_partitions= (split /=/,$free_partitions)[1];
$free_pars = split(/ /,$free_partitions);

$free_disk_list=qx'cat /etc/autobench.conf | grep "^[^#]\?DATS_FREE_DISKS"';
chomp $free_disk_list;
$free_disk_list= (split /=/,$free_disk_list)[1];
$free_disks = split(/ /,$free_disk_list);

$memory = `free | grep Mem`;
$memory = (split /\s+/,$memory)[1];

@eth_info=split(/\n/,`/sbin/ifconfig | grep "Link\\|inet"`);
my $if_name="";
foreach $_ (@eth_info) {
	chomp;
	s/^\s+//;
	my @temp = split /\s+/;

	if ($temp[1] =~ /Link/) {
		$if_name = $temp[0];
	}

	if ($temp[0] eq "inet") {	#Must check, could be inet6
		my $ip = (split /:/, $temp[1])[1];

		# If we don't have a primary ip or if the current primary
		# ip appears to be local and the new one does not, then
		# we make the new ip the primary ip. Otherwise, we add the
		# new ip to the list of secondary ips.
		if ($ip ne "127.0.0.1") { # Don't include localhost
			if (!$primary_ip) { 
				$primary_ip = $ip; 
				$primary_if = $if_name;
			}
			elsif (($primary_ip =~ /^192.168/ and !($ip =~ /^192.168/)) or
			       ($primary_ip =~ /^10\./ and !($ip =~ /^10\./))) { 
				push @ip_list, $primary_ip;
				push @if_list, $primary_if;
				$primary_ip = $ip;
				$primary_if = $if_name;
			} else {
				push @ip_list, $ip;
				push @if_list, $if_name;
			}
		}
		$if_name = "";
	}
}

sub list_ips
{
	my $cnt = 0;
	print "Number\tIP Address\tInterface\tNetwork name\n";
	print "0\t$primary_ip\t$primary_if\t$primary_netname\n";
	foreach my $ip (@ip_list) {
		my $if = $if_list[$cnt];
		my $netname = $netname_list[$cnt++];
		print "$cnt\t$ip\t$if\t$netname\n";
	}

	print "Enter the line number to change the ip info.\n";
	print "To set a new primary interface, use 'primary line_number'\n";
	print "To remove an interface, use 'remove line_number'\n";
	print "To re-list the ip list, use 'list'\n";
	print "To quit ip setup, enter a blank line.\n";
}

# Include a nice little text interface here listing all ip addresses
# and allowing the user to re-specify which is primary and add/delete
# addresses.
if (@ip_list and not $no_questions) {
	list_ips;
	
	my $input = " ";
	my $num_ips = @ip_list + 1;

	while ($input) {
		print "? ";
		$input = <STDIN>;
		chomp $input;
		if ($input eq "list") { list_ips; } 
		elsif ($input =~ /^remove/) {
			my $line_no = (split / /, $input)[1];
			if ($line_no < 0 or $line_no >= $num_ips) {
				print "Invalid number.\n";
				next;
			}
			if ($line_no == 0) {
				$primary_ip = shift @ip_list;
				$primary_if = shift @if_list;
				shift @netname_list;
			} else {
				$line_no--;
				splice @ip_list, $line_no, 1;
				splice @if_list, $line_no, 1;
				splice @netname_list, $line_no, 1;
			}
		} elsif ($input =~ /^primary/) {
			my $line_no = (split / /, $input)[1];
			if ($line_no < 1 or $line_no >= $num_ips) {
				print "Invalid number.\n";
				next;
			}
			$line_no--;
			my $temp_ip = $primary_ip;
			my $temp_if = $primary_if;
			$primary_ip = $ip_list[$line_no];
			$primary_if = $if_list[$line_no];
			$ip_list[$line_no] = $temp_ip;
			$if_list[$line_no] = $temp_if;
		} elsif ($input =~ /^\d+$/) {
			my $line_no = $input;
			my $new_ip;
			my $new_if;
			my $new_name;
			my $old_ip = $line_no ? $ip_list[$line_no - 1] : $primary_ip;
			my $old_if = $line_no ? $if_list[$line_no - 1] : $primary_if;
			my $old_name = $line_no ? $netname_list[$line_no - 1] : "primary";
			while (not $new_ip =~ /^$IP_REGEX$/) {
				print "IP Address: [$old_ip] ";
				$new_ip = <STDIN>;
				chomp $new_ip;
				$new_ip = $new_ip ? $new_ip : $old_ip;
			}
		
			while (not $new_if or length $new_if > $IF_LEN) {
				print "Interface: [$old_if] ";
				$new_if = <STDIN>;
				chomp $new_if;
				$new_if = $new_if ? $new_if : $old_if;
			}
			
			while (not $new_name or length $new_name > $NETNAME_LEN) {
				print "Network name: [$old_name] ";
				$new_name = <STDIN>;
				chomp $new_name;
				$new_name = $new_name ? $new_name : $old_name;
			}
			
			if ($line_no) {
				$ip_list[$line_no - 1] = $new_ip ? $new_ip : $old_ip;
				$if_list[$line_no - 1] = $new_if ? $new_if : $old_if;
				$netname_list[$line_no - 1] = $new_name ? $new_name : $old_name;
			} else {
				$primary_ip = $new_ip ? $new_ip : $old_ip;
				$primary_if = $new_if ? $new_if : $old_if;
			}
		} elsif ($input) {
			print "Invalid option. use 'list' to show available uses.\n";
		}
	}
}

if (open (MCH_INFO, "> machine.info")) {
	print MCH_INFO "machine_name=$machine_name\n";
	print MCH_INFO "dedicated=$ded\n";
	if ($c_port) { print MCH_INFO "console_port=$c_port\n"; }
	if ($m_port) { 	print MCH_INFO "mgmt_port=$m_port\n"; }
	print MCH_INFO "num_cpus=$num_cpus\n";
	print MCH_INFO "cpu_type=$cpu_type\n";
	print MCH_INFO "architecture=$arch\n";
	print MCH_INFO "speed=$speed\n";
	print MCH_INFO "cache=$cache\n";
	print MCH_INFO "memory=$memory\n";
	print MCH_INFO "free_partitions=$free_pars\n";
	print MCH_INFO "free_disks=$free_disks\n";
	print MCH_INFO "primary_ip=$primary_ip\n";
	print MCH_INFO "primary_interface=$primary_if\n";
	print MCH_INFO "primary_network_name=$primary_netname\n";
	print MCH_INFO "type=$machine_type\n";
	print MCH_INFO "state=$state\n";
	print MCH_INFO "ht_multiplier=$ht_mult\n";
	print MCH_INFO "reboot_delay=$reboot_delay\n";
	
	foreach (@ip_list) {
		my $if = shift @if_list;
		my $netname = shift @netname_list;
		if ( length($netname) == 0 ) {
			$netname = $machine_name."-".$if;
		}
		print MCH_INFO "secondary_ip=$_\n"; 
		print MCH_INFO "secondary_interface=$if\n";
		print MCH_INFO "secondary_network_name=$netname\n";
	}
	
	foreach my $pair (@distros) {
		my ($name, $root) = @$pair;
		print MCH_INFO "distro_name=$name\n";
		print MCH_INFO "distro_root=$root\n";
	}

	close MCH_INFO;
} else {
	print "Unable to open machine.info file for writing.\n";
	exit 1;
}
