#!/usr/bin/perl

# -----------------------------------------------------------------------------
#  Write node hardware statistics to master DB
# -----------------------------------------------------------------------------
#  Author: Alexey Tsibulbik
#  Modified by:
#  QA by:  
#  Copyright: videoNEXT Network solutions, Inc.
# -----------------------------------------------------------------------------

use strict;
use warnings;

use SKM::DB;
use Node::Conf ":all";
use Log::Log4perl "get_logger";
require "$ENV{APL}/common/bin/logger.patrol";

# CONS
my $APL = $ENV{APL};
my $DB_MAX_CONNECTS = 3; # Maximum attempts to connect to apl db
my $DB_CONNECT_INTERVAL = 10;
my $OVERLOAD = "$APL/var/cpu-overload";
my $SLEEP = 30;	# Main loop timeout
my $UNI = UNI;
my $NCORES = Count_Cores;

# VAR
my $log = get_logger('NEXTCAM::CONF::NODE_DB');
my $dbm;
my %dbs;
my $node_obj;

# SUB
sub db_master {
	for (my $i = 1; $i <= $DB_MAX_CONNECTS; $i++) {
		eval {
    			$dbm = DBMaster({
    		    	        PrintError => 0, 
    			        RaiseError => 1,
    			        AutoCommit => 0
    			});
		};
		if($@) {
        		$log->logdie("DB_MASTER: Attempt $i (final). Cannot connect to master: $@") if $i>=$DB_MAX_CONNECTS;
    			$log->error("Attempt $i. Cannot connect to master: $@");
    			$log->error('Sleep '. $i*$DB_CONNECT_INTERVAL . ' before next attempt');
    			sleep($i*$DB_CONNECT_INTERVAL);
		} else {
			last;
		}
	}
	
	eval {
		$dbs{INSERT_ATTR}=$dbm->prepare("insert into _obj_attr(obj,attr,val) values(?,?,?)");
		$dbs{UPDATE_ATTR}=$dbm->prepare("update _obj_attr set val=? where attr=? and obj=?");
		$dbs{GET_NODE_OBJ}=$dbm->prepare(
			"SELECT obj FROM _objs 
			WHERE name=? AND otype='D' AND subtype='N' and deleted=0"
		);
		# Get node objid
		$dbs{GET_NODE_OBJ}->execute($UNI);
    		$node_obj = ($dbs{GET_NODE_OBJ}->fetchrow_array)[0];
    		die "Unable to detect node ObjId" unless $node_obj;

	};
        $log->logdie("SQLERR:$@") if $@;
}

sub collect_hw_stats
{
	my %stat;
        my $statstr = "";
        # Load Average
        if(open(AVG, "/proc/loadavg")) {
    		my (@avg) = (split(/\s/,<AVG>))[0..2];
		close AVG;
		$stat{LOAD_AVERAGE} = "@avg";
		# Count load average per core
		my @avg_c = map { sprintf("%.2f", $_ / $NCORES) } @avg;
		$stat{LOAD_AVERAGE_CORE} = "@avg_c";
	}
	# Uptime
        if (open(UP, "/proc/uptime")) {
		my ($up) = split(/\s/,<UP>);
		close UP;
		$stat{UPTIME} = int($up);
	}
	# Disk stats
        if (open DF, "/bin/df -P / /var /var/sarch /vasm/cache 2>/dev/null |") {
		my %df = map {($2,$1) if /(\d+)%\s+(\S+)\s*$/} grep {/\d%/} <DF>;
		close DF;
		while (my ($disk,$usg) = each %df) {
		    if    ($disk eq '/')           { $stat{ROOT_USAGE}   = $usg }
		    elsif ($disk eq '/var')        { $stat{VAR_USAGE}    = $usg }
		    elsif ($disk eq '/var/sarch')  { $stat{APLVAR_USAGE} = $usg }
		    elsif ($disk eq '/vasm/cache') { $stat{CACHE_USAGE}  = $usg }
		}
        }
        
        # CPU overload flag
        if (-f $OVERLOAD and open FH, $OVERLOAD) {
    	    my $load = <FH>;
    	    close FH;
    	    $stat{CPU_OVERLOAD} = $load;
        }
        else {
    	    $stat{CPU_OVERLOAD} = 0;
        }
        
        return \%stat;
}

sub write_node_attrs
{
	my $attrs = shift;
	my $is_stat = shift || 1;
	
	if (not $attrs or ref($attrs) ne 'HASH') {
		$log->warn("Invalid argument. Hashref expected, $attrs given");
		return;
	}
	while ( my ($attr, $val) = each %$attrs ) {
		eval {
			my $name = $is_stat ? "STAT_$attr" : $attr;
			$dbs{UPDATE_ATTR}->execute($val, $name, $node_obj);
    			my $rows = $DBI::rows;
    			if($rows < 1) {        # stat record is missing. Inserting ..
    				$dbs{INSERT_ATTR}->execute($node_obj, $name, $val);
    			}
    		};
    		$log->error("Cannot write node attr to DB for node UNI=$UNI: $@") if $@;
	}
	
	eval {
		$dbm->commit;
	};
	$log->logdie("Node attrs commit failed for node UNI=$UNI: $@") if $@;
}

sub main
{
	$log->info("db_node starts");
	
	db_master;
    
	while (1) {
		write_node_attrs(collect_hw_stats);
		sleep $SLEEP;
	}
}

# MAIN
main;

END {
	eval { $dbm->disconnect } if $dbm;
}
