#!/usr/bin/perl -w -I/usr/local/stow/lib/perl/5.8.4/
#
# Quick and ugly script to create some rrdtool files...
# --JDB@comx.dk
#
use strict;
use POSIX;
use Carp;
use RRDs;
use Data::Dumper;

# The $STEP value is the base interval in seconds with which 
# data will be fed into the RRD.
our $STEP=30;
our @rrd_step_args = ("-s", $STEP);

# heartbeat:
# ----------
# A long heartbeat can span multiple "steps", which means it is
# acceptable to have multiple PDPs calculated from a single sample
#our $heartbeat = $STEP + 5;
our $heartbeat = $STEP * 4;

# xff:
# ---
# The xfiles factor defines what part of a con solidation interval may
# be made up from *UNKNOWN* data while the consolidated value is still
# regarded as known.
our $xff=0.5;


# Time period helper functions
# ----------
sub days($) {
    my $x = shift;
    my $oneday=60*60*24;
    return ($x * $oneday);
}

sub hours($) {
    my $x = shift;
    my $onehour=60*60;
    return ($x * $onehour);
}

sub minuts($) {
    my $x = shift;
    my $oneminut=60;
    return ($x * $oneminut);
}

sub sec($) {
    my $x = shift;
    return $x;
}

# RRDtool definitions:
# ===================
#  Taken from: rrdcreate(1)
#
#  "steps":
#    defines how many of these primary data points are used to build a 
#    consolidated data point which then goes into the archive
#
#  "rows":
#    defines how many generations of data values are kept in an RRA.
# 

#####
# Calc "rows" to store $step seconds averages in a given period (seconds)
# ===========
# rows( $period_sec, $step_sec )
# rows( $period_sec, $average_sec )
# rows_for_period_in_steps
sub rows($$) {
    my $period_sec = shift;
    my $step_sec   = shift;

    if ($step_sec < $STEP) {
	$step_sec = $STEP;
	carp "WARNING: Trying to get higher resolution that the possible STEP size";
    }

    my $rows=$period_sec / $step_sec;
    #print "rx:$rows\n";
    return ceil($rows)
}
#my $r=rows(hours(48), sec(30))
#print "r:$r\n";

####
# Calc number of "steps"
# ======================
# To average over a period, calc the number of "steps" or data points that are 
# needed to build a consolidated data point which then goes into the archive.
#
# This is based upon the $STEP value used by RRDtool (which is the base interval 
# in seconds with which data will be fed into the RRD).
#
sub steps($) {
    my $average_period = shift;
    my $step_sec       = $STEP;

    my $steps=$average_period / $step_sec;
    #print "sx:$steps\n";
    return ceil($steps)
}
#my $s=steps(minuts(5));

# Calc both steps and rows
# ----
# steps_rows($average, $period)
sub steps_rows($$) {
    my $average = shift;
    my $period  = shift;

    my $steps = steps($average);
    my $rows  = rows($period, $average);

    my $string = "$steps:$rows";
    return $string;
}

#print "RRA:AVERAGE:$xff:" . steps_rows(minuts(5),hours(24)) . "\n";
#print "B:" . steps_rows(minuts(5),hours(48)) . "\n";
#print "C:" . steps_rows(minuts(5),days(2)) . "\n";
#print "D:" . steps_rows(sec(30),days(2)) . "\n";
#print "E:" . steps_rows(sec($STEP),days(7)) . "\n";

sub get_RRA($$$) {
    my $average = shift;
    my $period  = shift;
    my $type    = shift; #eg. AVERAGE or MAX
    
    my $start      = "RRA:$type:$xff:";
    my $steps_rows = steps_rows($average, $period);
    my $string = $start . $steps_rows;
    return $string;
}

# Defining: Round Robin Archives
our @RRAs;

push @RRAs, get_RRA(sec($STEP), days(14), "AVERAGE");

push @RRAs, get_RRA(minuts(1)  , days(31), "AVERAGE");
push @RRAs, get_RRA(minuts(2)  , days(31), "MAX");
push @RRAs, get_RRA(minuts(2)  , days(31), "MIN");

push @RRAs, get_RRA(minuts(5) , days(62), "AVERAGE");
push @RRAs, get_RRA(minuts(5) , days(62), "MAX");
push @RRAs, get_RRA(minuts(10), days(62), "MIN");

push @RRAs, get_RRA(hours(0.5) , days(365), "AVERAGE");
push @RRAs, get_RRA(hours(0.5) , days(365), "MAX");
push @RRAs, get_RRA(hours(1.0) , days(365), "MIN");

#print @RRAs;
#print "\nThe RRAs:\n";
#foreach my $x (@RRAs) {
#    print "$x\n";
#}


our $BASEDIR="/var/lib/rrdcollect";
#our $BASEDIR="/tmp/rrdtest";

sub create_rrdfile($%) {
    my $filename    = shift;
    my $DS_hash_ref = shift;

    my %DS_hash     = %{$DS_hash_ref};
    $filename ="$BASEDIR/$filename";

    my @data_sources;

    if ( -f "$filename" ) {
	print "RRDcreate: File already exist: $filename.\n";
	return;
    }
    print "RRDcreate: $filename\n";

    foreach my $DS_name (keys %DS_hash) {
	my $type   = $DS_hash{$DS_name};
	my $string = "DS:$DS_name:$type:$heartbeat:0:U";
	#print "$string\n";
	push @data_sources, $string;
    }

    RRDs::create "$filename", @rrd_step_args, @data_sources, @RRAs;
    my $ERROR = RRDs::error;
    if ($ERROR) {
        croak "ERROR - Unable to create RRDfile \"$filename\": $ERROR\n";
    }
}


#our @data_names;

# file: stat.rrd
# --------------
my %DS_stat=
(
 'cpu_user'    => "COUNTER",
 'cpu_nice'    => "COUNTER",
 'cpu_system'  => "COUNTER",
 'cpu_idle'    => "COUNTER",
 'cpu_iowait'  => "COUNTER",
 'cpu_irq'     => "COUNTER",
 'cpu_softirq' => "COUNTER",
 'ctxt'        => "COUNTER",
 'page_in'     => "COUNTER",
 'page_out'    => "COUNTER",
 'processes'   => "COUNTER",
 'swap_in'     => "COUNTER",
 'swap_out'    => "COUNTER",
);
create_rrdfile("stat.rrd", \%DS_stat);


# file: ethX.rrd
# --------------
#my $file="ethX.rrd";
#my @data_names= qw( bytes_in pkts_in bytes_out pkts_out );
my %DS_eth = 
(
 'bytes_in'  => "COUNTER",
 'pkts_in'   => "COUNTER",
 'bytes_out' => "COUNTER",
 'pkts_out'  => "COUNTER", 
);

create_rrdfile("eth0.rrd", \%DS_eth);
create_rrdfile("eth1.rrd", \%DS_eth);


# file: rt_cache_cpuXX.rrd
# --------------
my %rt_cache = 
(
 'entries'         => "GAUGE",
 'in_hit'          => "COUNTER",
 'in_slow_tot'     => "COUNTER",
 'in_slow_mc'      => "COUNTER",
 'in_no_route'     => "COUNTER",
 'in_brd'          => "COUNTER",
 'in_martian_dst'  => "COUNTER",
 'in_martian_src'  => "COUNTER",
 'out_hit'         => "COUNTER",
 'out_slow_tot'    => "COUNTER",
 'out_slow_mc'     => "COUNTER",
 'gc_total'        => "COUNTER",
 'gc_ignored'      => "COUNTER",
 'gc_goal_mis'     => "COUNTER",
 'gc_dst_overflow' => "COUNTER",
 'in_hlist_search' => "COUNTER",
 'out_hlist_search'=> "COUNTER",
);

create_rrdfile("rt_cache_cpu01.rrd", \%rt_cache);
create_rrdfile("rt_cache_cpu02.rrd", \%rt_cache);
create_rrdfile("rt_cache_cpu03.rrd", \%rt_cache);
create_rrdfile("rt_cache_cpu04.rrd", \%rt_cache);

my %DS_meminfo =
(
 'MemTotal'	=> "GAUGE",
 'MemFree'	=> "GAUGE",
 'Buffers'	=> "GAUGE",
 'Cached'	=> "GAUGE",
 'SwapCached'	=> "GAUGE",
 'Active'	=> "GAUGE",
 'Inactive'	=> "GAUGE",
 'HighTotal'	=> "GAUGE",
 'HighFree'	=> "GAUGE",
 'LowTotal'	=> "GAUGE",
 'LowFree'	=> "GAUGE",
 'SwapTotal'	=> "GAUGE",
 'SwapFree'	=> "GAUGE",
 'Dirty'	=> "GAUGE",
 'Writeback'	=> "GAUGE",
 'Mapped'	=> "GAUGE",
 'Slab'		=> "GAUGE",
 'CommitLimit'	=> "GAUGE",
 'Committed_AS'	=> "GAUGE",
 'PageTables'	=> "GAUGE",
 'VmallocTotal'	=> "GAUGE",
 'VmallocUsed'	=> "GAUGE",
 'VmallocChunk'	=> "GAUGE",
 );

create_rrdfile("meminfo.rrd", \%DS_meminfo);


# file: ip_conntrack_cpuXX.rrd
# --------------
my %ip_conntrack = 
(
 'entries'         => "GAUGE",
 'searched'	   => "COUNTER",
 'found'	   => "COUNTER",
 'new'		   => "COUNTER",
 'invalid'	   => "COUNTER",
 'ignore'	   => "COUNTER",
 'delete'	   => "COUNTER",
 'delete_list'	   => "COUNTER",
 'insert'	   => "COUNTER",
 'insert_failed'   => "COUNTER",
 'drop'		   => "COUNTER",
 'early_drop'	   => "COUNTER",
 'icmp_error'	   => "COUNTER",
 'expect_new'	   => "COUNTER",
 'expect_create'   => "COUNTER",
 'expect_delete'   => "COUNTER",
);

create_rrdfile("ip_conntrack_cpu01.rrd", \%ip_conntrack);
create_rrdfile("ip_conntrack_cpu02.rrd", \%ip_conntrack);
create_rrdfile("ip_conntrack_cpu03.rrd", \%ip_conntrack);
create_rrdfile("ip_conntrack_cpu04.rrd", \%ip_conntrack);


# file: arp_cache_cpuXX.rrd
# --------------
my %arp_cache = 
(
 'entries'	    => "GAUGE",
 'allocs'	    => "COUNTER",
 'destroys'	    => "COUNTER",
 'hash_grows'	    => "COUNTER",
 'lookups'	    => "COUNTER",
 'hits'		    => "COUNTER",
 'res_failed'	    => "COUNTER",
 'rcv_probes_mcast' => "COUNTER",
 'rcv_probes_ucast' => "COUNTER",
 'periodic_gc_runs' => "COUNTER",
 'forced_gc_runs'   => "COUNTER",
);

create_rrdfile("arp_cache_cpu01.rrd", \%arp_cache);
create_rrdfile("arp_cache_cpu02.rrd", \%arp_cache);
create_rrdfile("arp_cache_cpu03.rrd", \%arp_cache);
create_rrdfile("arp_cache_cpu04.rrd", \%arp_cache);



# file: softnet_stat_cpuXX.rrd
# ============================
# The contents of /proc/net/softnet_stat is not really documented,
# reading the code and talking to Robert Olsson gave this.
#
# "time_squeeze": Det jag ville se time_squeeze dvs när RX-softirq:en
#                 kört sin budget. Det säger att du inte nått pollläge.
#
# "cpu_collision": Du har en del CPU-collisions (sista kol) det är när
#                  den CPU låst TX-pathen för den andra.
#
# netdevice.h:
# ------------
# struct netif_rx_stats
# {
# 	unsigned total;
# 	unsigned dropped;
# 	unsigned time_squeeze;
# 	unsigned cpu_collision;
# };
#
# linux-2.6.16.7/net/core/dev.c:
# ------------------------------
#static int softnet_seq_show(struct seq_file *seq, void *v)
#{
#	struct netif_rx_stats *s = v;
#
#	seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
#		   s->total, s->dropped, s->time_squeeze, 0,
#		   0, 0, 0, 0, /* was fastroute */
#		   s->cpu_collision );
#	return 0;
#}
#
# total    droppped tsquz    Throttl  FR_hit   FR_succe FR_defer FR_def_o cpu_coll
# be03faae 00000000 000050c6 00000000 00000000 00000000 00000000 00000000 00000000
#
my %softnet_stat =
(
 'total'         => "COUNTER",
 'dropped'       => "COUNTER",
 'time_squeeze'  => "COUNTER",
# 'throttle'      => "COUNTER",
# 'FR_hit'        => "COUNTER",
# 'FR_succes'     => "COUNTER",
# 'FR_defer'      => "COUNTER",
# 'FR_def_o'      => "COUNTER",
 'cpu_collision' => "COUNTER"
);

create_rrdfile("softnet_stat_cpu01.rrd", \%softnet_stat);
create_rrdfile("softnet_stat_cpu02.rrd", \%softnet_stat);
create_rrdfile("softnet_stat_cpu03.rrd", \%softnet_stat);
create_rrdfile("softnet_stat_cpu04.rrd", \%softnet_stat);

my %interrupts =
(
 'eth0_cpu01'		=> "COUNTER",
 'eth0_cpu02'		=> "COUNTER",
 'eth0_cpu03'		=> "COUNTER",
 'eth0_cpu04'		=> "COUNTER",

 'eth1_cpu01'		=> "COUNTER",
 'eth1_cpu02'		=> "COUNTER",
 'eth1_cpu03'		=> "COUNTER",
 'eth1_cpu04'		=> "COUNTER",

 'NMI_cpu01'		=> "COUNTER",
 'NMI_cpu02'		=> "COUNTER",
 'NMI_cpu03'		=> "COUNTER",
 'NMI_cpu04'		=> "COUNTER",

 'LOC_cpu01'		=> "COUNTER",
 'LOC_cpu02'		=> "COUNTER",
 'LOC_cpu03'		=> "COUNTER",
 'LOC_cpu04'		=> "COUNTER",

 'RES_cpu01'		=> "COUNTER",
 'RES_cpu02'		=> "COUNTER",
 'RES_cpu03'		=> "COUNTER",
 'RES_cpu04'		=> "COUNTER",

 'CAL_cpu01'		=> "COUNTER",
 'CAL_cpu02'		=> "COUNTER",
 'CAL_cpu03'		=> "COUNTER",
 'CAL_cpu04'		=> "COUNTER",

 'TLB_cpu01'		=> "COUNTER",
 'TLB_cpu02'		=> "COUNTER",
 'TLB_cpu03'		=> "COUNTER",
 'TLB_cpu04'		=> "COUNTER",
);

create_rrdfile("interrupts.rrd", \%interrupts);
