#!/usr/local/lp/lwprov/bin/perl
#============================================================================
# NAME:  xvps_xen_autosetup.pl
#
# DESCRIPTION: Configure server hostname, networking, and root password.
#
# AUTHOR:  Scott Sullivan (ssullivan), ssullivan@liquidweb.com
# COMPANY:  Liquid Web, Inc.
#============================================================================

use strict;
use warnings;
use Time::localtime;

#
# Configuration 
#############################################################################

my %conf = (
	version => '0.7.0',
	lock_file => '/root/xvps_autosetup.lock',
	log_file => '/var/log/xvps_autosetup.log',
	ptype_file => '/usr/local/lp/etc/PTYPE',
	conf_file => '/root/xvps_autosetup.conf',
	halted_file => '/root/xvps_autosetup_HALTED',
);

my %api_store = ();

#
# Methods
#############################################################################

sub verify_nic_intel {
        my $opts = shift;

        system("which ethtool &> /dev/null");
        if ( $? != 0 ) {
                logger('c','ethtool is not in the $PATH. Unable to determine link speed.');
        }

	my $ethtool_output = `ethtool -i $opts->{interface}`;
        if ( $ethtool_output =~ /driver:\s+e1000/i || $ethtool_output =~ /driver:\s+igb/i || $ethtool_output =~ /driver:\s+ixgbe/i ) {
                logger('i',"Good; $opts->{interface} is using an Intel driver");
        }
        else {
                # Place file signifying autosetup intentionally halted.
                open(FH,">$conf{'halted_file'}") or logger('c', "Unable to place $conf{'halted_file'}: $!");
                close(FH);i 
                logger('c',"$opts->{interface} is not an Intel NIC; Halting autosetup.");                 
	}
}

sub verify_link_speed {
        my $opts = shift;

        system("which ethtool &> /dev/null");
        if ( $? != 0 ) {
                logger('c','ethtool is not in the $PATH. Unable to determine link speed.');
        }

        if ( `ethtool $opts->{interface}` =~ /\s+Speed:\s+1000Mb\/s/i || `ethtool $opts->{interface}` =~ /\s+Speed:\s+10000Mb\/s/i ) {
                logger('i',"Good; $opts->{interface} is at least 1GB");
        }
        else {
                # Place file signifying autosetup intentionally halted.
                open(FH,">$conf{'halted_file'}") or logger('c', "Unable to place $conf{'halted_file'}: $!");
                close(FH);
                logger('c',"$opts->{interface} is not 1GB; Halting autosetup.");
	}
}

sub read_conf_file {
	my %config;
	open my $config, '<', "$conf{'conf_file'}" || logger('c',"Can't open $conf{'conf_file'}: $!");
	while(<$config>) {
		chomp;
		s/^\s+//;  # rm leading white
		s/\s+$//;  # rm trailing white
		next unless length;
		(my $key, my @value) = split /\s*=\s*/, $_;
		$config{$key} = join '=', @value;
	}
	return \%config;
}

sub populate_api_store {
	my $zone = shift;
	if ( json_ok() && ua_ok() ) {
		my $ptype = get_ptype();
		my $args = {
			type => $ptype,
			zone => $zone,
		};
		my $api_ret = call('provisioning/parent/initialize.json',$args);
		if ( $api_ret->{assignment} && $api_ret->{subaccnt} ) {
			$api_store{'hostname'}	= $api_ret->{subaccnt}->{domain};
			$api_store{'ip_addr'} 	= $api_ret->{subaccnt}->{ip};
			$api_store{'uniq_id'}	= $api_ret->{subaccnt}->{uniq_id};
			$api_store{'netmask'}	= $api_ret->{assignment}->{netmask};
			$api_store{'gateway'}	= $api_ret->{assignment}->{gateway};
			$api_store{'vlan'}	= $api_ret->{assignment}->{vlan};
		} 
		else {
			logger('c',"provisioning/parent/initialize.json bad response! Error class: $api_ret->{error_class} Details:\n$api_ret->{full_message}");
		}
	}
}

sub get_first_line {
	my $file = shift;
	open(FH,"<$file") or logger('c',"Couldn't open file ($file): $!");
	my $char = <FH>;
	chomp($char);
	return $char;
}

sub get_ptype {
	my $ptype = get_first_line("$conf{'ptype_file'}");
	return $ptype;
}

sub call {
	my $method = shift or logger('c',"call() called with no method given!");
	my $params = shift or logger('c',"call() called with no params given!");
	require LWP::UserAgent;
	require JSON;
	my $ua = LWP::UserAgent->new;
	$ua->ssl_opts( verify_hostname => 0 );
    $ua->timeout(120);
	$ua->agent("xvps_autosetup-$conf{'version'}" . $ua->agent);
	my $conf_file = read_conf_file();  
	my $api_host = $conf_file->{'api_url'};
	my $api_string = "$api_host/$method";
	my $req = HTTP::Request->new(POST => $api_string);
	my $username = $conf_file->{'api_user'};
	my $password = $conf_file->{'api_pass'};
	$req->authorization_basic($username,$password);
	my $parser = JSON->new->utf8(1);
	my $json = $parser->encode({
		metadata => { ip => $ENV{REMOTE_ADDR} },
		params => $params,
	});
	$req->content($json);
	my $response = $ua->request($req);
	my $code = $response->code;
	my $content = $response->content;
	if ( $code == 200 ) {
		logger('i',"Good; got response code 200 from API.");
		my $results  = $parser->decode($content);
		return $results;
	}
	else {
		logger('c',"Bad; got response code $code from API. Content:\n$content");
	}
}

sub ua_ok {
	my $ret = (eval { require LWP::UserAgent; 1 }) || 0;
	if ( $ret == 0 ) {
		logger('c',"Couldn't load LWP::UserAgent!");
	}
	return $ret;
}

sub json_ok {
	my $ret = (eval { require JSON; 1 }) || 0;
	if ( $ret == 0 ) {
		logger('c',"Couldn't load the JSON module!");
	}
	return $ret;
}

sub stop_agent {
	system("screen -X -S pagent quit");
	logger('i','Sent p-agent quit.');
}

sub write_billUID {
	logger('c','write_billUID() unable to determine UNIQ_ID.') unless ( $api_store{'uniq_id'} );
	open(FH,">/usr/local/lp/etc/lp-UID") or logger('c',"Unable to open /usr/local/lp/etc/lp-UID for write: $!");
	print FH "$api_store{'uniq_id'}\n";
	close(FH);
	logger('i','Configured /usr/local/lp/etc/lp-UID');	
}

sub set_hosts {
	my $hostname = shift or logger('c',"set_hosts() didnt get hostname to set!");
	my $ip_address = shift or logger('c',"set_hosts() didnt get ip_address to set!");
	my @shortname = split(/\./, $hostname);
	open(FH,">/etc/hosts") or logger('c',"Unable to open /etc/hosts! $!");
	my $contents = "127.0.0.1       localhost localhost.localdomain localhost4 localhost4.localdomain4
::1	     localhost localhost.localdomain localhost6 localhost6.localdomain6
$ip_address      $hostname $shortname[0]";
	print FH "$contents";
	close(FH);
	logger('i',"Configured /etc/hosts.");
}

sub set_hostname {
	my $hostname = shift or logger('c',"set_hostname() didnt get hostname to set!");
	my $stderr = `sed -i "s/HOSTNAME=.*/HOSTNAME=$hostname/" /etc/sysconfig/network 2>&1 1>/dev/null`;
	if ( $? != 0 ) {
		chomp($stderr);
		logger('c',"Error setting hostname; Got: $stderr");
	}
	logger('i',"Set system hostname to $hostname.");
}

sub config_networking {
	my $config_file = shift or logger('c',"config_networking() didnt get config_file to modify!");
	my $contents = shift or logger('c',"config_networking() didnt get contents to place!");
	# Get eth0 HWADDR
	for my $hwaddr ( split /^/, `ip addr show eth0` ) { 
		if ( $hwaddr =~ /^\s+link\/ether\s+(\S+)\s+/i ) { 
	        	$contents .= uc"HWADDR=$1\n";
	      	}   
	}   
	open(FH,">$config_file") or logger('c',"Unable to open $config_file: $!");
	print FH "$contents";
	close(FH);
	logger('i',"Configured $config_file.");
}

sub rest_networking {
	system("/etc/init.d/network restart");
	logger('i','Restarted networking.');
}

sub place_lock {
	open(FH,">$conf{'lock_file'}") or logger('c',"Unable to place lock file! Got: $!");
	close(FH);
}

sub rm_lock {
	if ( -e $conf{'lock_file'} ) {
		unlink($conf{'lock_file'});
	}
}

sub do_exit {
	rm_lock();
	exit 1;
}

sub clean_self {
	unlink("$conf{'conf_file'}");
	unlink('/root/xvps_xen_autosetup.pl');
}

sub get_time {
	my $cur_time = ctime();
	return $cur_time;
}

sub logger {
	my $level = shift or logger('i',"logger() called but log level not given!");
	my $msg = shift or logger('i',"logger() called but no message to log!");
	my $time_stamp = get_time();
	my $label = ( $level eq 'c' ) ? 'CRIT:' : 'INFO:';
	open(LOGFILE,">>$conf{'log_file'}") or die "Unable to open $conf{'log_file'} for writing! $!";
	print LOGFILE "[ $time_stamp ] $label $msg\n";
	close(LOGFILE);
	if ( $level eq 'c' ) {
		print STDERR "\n$0 encountered one or more fatal errors. Be sure to review the log ($conf{'log_file'}).\n";
		do_exit();
	}
}

#
# Main
#############################################################################

my $zone = $ARGV[0] || logger('c',"Required argument zone was not passed!");

verify_nic_intel({ interface => 'eth0' });
verify_link_speed({ interface => 'eth0' });
place_lock();
populate_api_store("$zone");
stop_agent();
set_hosts("$api_store{'hostname'}","$api_store{'ip_addr'}");
set_hostname("$api_store{'hostname'}");
write_billUID();
my $content .= <<END;
DEVICE=eth0
BOOTPROTO=static
ONBOOT=yes
END
config_networking('/etc/sysconfig/network-scripts/ifcfg-eth0',"$content");
$content .= <<END;
DEVICE=vlan$api_store{vlan}
PHYSDEV=eth0
BOOTPROTO=static
ONBOOT=yes
VLAN=yes
VLAN_NAME_TYPE=VLAN_PLUS_VID_NO_PAD
ONBOOT=yes
TYPE=Ethernet

IPADDR=$api_store{ip_addr}
NETMASK=$api_store{netmask}
GATEWAY=$api_store{gateway}
END
config_networking("/etc/sysconfig/network-scripts/ifcfg-vlan$api_store{'vlan'}","$content");
rest_networking();
rm_lock();
clean_self();
