#!/usr/bin/perl
# -*- mode: perl; indent-tabs-mode: t; perl-indent-level: 8 -*-
#
# Author: Andrew Theurer
#
# The purpose of this script is to convert sar data
# found in sar-stdout.txt into either
# (depending on how this script is invoked):
#
#   CSV files and statically generated graphs
#   with embedded javascript, using d3.js libraries
#
# Usage:
#
# sar-postprocess <dir>
#
#   dir = directory where sar-stdout.txt can be found
#
# Note:
#
#   sar-stdout.txt must be generated with "sar -A <interval>"

use strict;
use warnings;

use lib $ENV{'pbench_lib_dir'};
no lib ".";
use GenData qw(gen_data);
use SysStat qw(get_sar_cpumode_attributes build_cpu_topology get_cpu_socket_core);

my $dir = shift;

die "Non-default options, skipping post-processing for sar" if (-f $dir . "/sar.options");

my @cpu_topology;
my $interval; # sampling interval

my %sar;
my $prev_time = "";
my $date_ms;
my %graph_type;
my $line_num = 0;
my $line;
my $time;
my $timestamp_ms;
my $extra_day_ms= 0;  # the extra amount of time in ms we need to add as the date recorded goes from one day to the next
my %graph_threshold;
my @cpumode_attributes = get_sar_cpumode_attributes;
my %cpu_modes;
my @nonbusy = (qw(idle iowait steal));
my @busy = grep(!/^idle?|^iowait?|^steal?/, @cpumode_attributes);
$cpu_modes{'idle'} = \@nonbusy;
$cpu_modes{'busy'} = \@busy;
$graph_threshold{network_l2}{network_packets_sec} = 300;
$graph_threshold{network_l2}{carrier_errors} = 1;
$graph_threshold{network_l2}{drops_sec} = 1;
$graph_threshold{network_l2}{errors_sec} = 1;
$graph_threshold{network_l2}{fifo_overrun_errors} = 1;
$graph_threshold{network_l2}{frame_alignment_errors} = 1;
$graph_threshold{network_l2}{network_Mbits_sec} = 1;
$graph_threshold{network_l2}{network_compressed_packets_sec} = 1;
$graph_threshold{network_l2}{network_multicast_packets_sec} = 1;

# we'll force sar into a LANG=C environment: two-digit year on the first line,
# 24-hour time with *no* AM/PM designation in the timestamp.
# but calc_timestamp() and the TSRE below are backwards-compatible and can
# deal with AM/PM
# Usage example: $timestamp_ms = calc_timestamp($time);
sub calc_timestamp {
	my $this_time = $_[0];
	if ( $prev_time eq "" ) { # if this is the first time
		$prev_time = $this_time;
	}
	# time format: HH:MM:SS
	# 24-hour format,*no* AM/PM designation
	my $hourminsec = $this_time;
	$hourminsec =~ /(\d+):(\d+):(\d+)\s?(\w\w)?/;
	my $hour = $1;
	my $min = $2;
	my $sec = $3;
	my $this_ampm = $4;
	$prev_time =~ /(\d+):\d+:\d+\s?(\w\w)?/;
	my $prevhour = $1;
	my $prev_ampm = $2;

	# check for AM/PM first and if it's not there assume 24-hour
	# clock and compare hours.
	# the hour check is not entirely correct but it's more
	# than sufficient for our purposes: it assumes that successive
	# timestamps differ by less than 24 hours.
	if ( (defined($this_ampm) and defined($prev_ampm) and ( $this_ampm eq "AM") and ( $prev_ampm eq "PM")) ||
	     ( $prevhour > $hour ) ) {
		# since we have rolled over to the next day, add enough ms to extra_day_msequal to 1 day
		$extra_day_ms += (24 * 60 * 60 * 1000);
	}
	# Calculate the milliseconds for just the time for today
	if (defined($this_ampm) and ($this_ampm eq "PM")) {
		$hour += 12;
	}
	my $today_time_ms = ($hour * 60 * 60 + $min * 60 + $sec) * 1000;
	$prev_time = $this_time;
	return int ($date_ms + $extra_day_ms + $today_time_ms);
}


# read the sar.txt and collect stats
open(SAR_TXT, "$dir/sar-stdout.txt") || die "could not find $dir/sar-stdout.txt\n";

# The first line contains the date:
#    Linux 3.16.6-200.fc20.x86_64 (a.foo.example.com) 	02/25/2015 	_x86_64_	(8 CPU)
# Subsequent lines are of the form:
#    05:48:49 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
# containing only the time, so we need to combine the two to get a meaningful timestamp.

$line = <SAR_TXT>;
chomp $line;
if ( $line =~ /\S+\s\S+\s\S+\s+(\d\d\/\d\d\/(\d\d)?\d\d)(.*)/ ) {
	$date_ms = 1000 * `date --date="$1" +%s`;
} else {
	# TBD: default is today - should we bail instead?
	# TBD: what if the first line does not match? do we keep looking? or die?
	# die("Bad sar-stdout.txt file format: first line should have the form: Linux 3.16.6-200.fc20.x86_64 (a.foo.example.com) 	02/25/2015 	_x86_64_	(8 CPU)");
	$date_ms = "";
}

# timestamp regexp - optional AM/PM designator, but don't catch it as a group
my $tsre = qr/\d+\:\d+\:\d+(?:\s\w\w)?/;

my $mode="none";
while ($line = <SAR_TXT>) {
	$line_num++;
	chomp $line;

	if ( $line eq "" ) {
		$mode = "none";
		next;
	}
	# 03:25:51 AM     CPU      %usr     %nice      %sys   %iowait    %steal      %irq     %soft    %guest    %gnice     %idle
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+CPU\s+%usr\s+%nice\s+%sys\s+%iowait\s+%steal\s+%irq\s+%soft\s+%guest\s+%gnice\s+%idle/ )) {
		$mode = "cpu_util";
		next;
	}
	# 03:25:56 AM     all      0.01      0.00      0.01      0.00      0.00      0.00      0.00      0.00      0.00     99.98
	# 03:25:56 AM       0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00    100.00
	if (( $mode eq "cpu_util" ) &&
	               # timestamp              pu_id   %user     %nice        %sys         %iowait      %steal       %irq         %soft        %guest        %gnice      %id
	    ( $line =~ /($tsre)\s+(\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)/ )) {
		$line =~ s/($tsre)\s+//;
		$time = $1;
		(my $cpuid, my @values) = split(/\s+/, $line);
		my %cpumode_util;
		(my $socket, my $core) = get_cpu_socket_core($cpuid, \@cpu_topology);
                @cpumode_util{@cpumode_attributes} = @values;
		$timestamp_ms = calc_timestamp($time);
		$cpuid = sprintf "%02d", $cpuid;
		my $cpulabel = "cpu_" . $cpuid;
		my $busy = $cpumode_util{'usr'} + $cpumode_util{'nice'} + $cpumode_util{'sys'} + $cpumode_util{'irq'} + $cpumode_util{'soft'} + $cpumode_util{'guest'} + $cpumode_util{'gnice'};
		for my $mode (@cpumode_attributes) {
			$sar{per_cpu}{$cpulabel}{$mode}{$timestamp_ms} = $cpumode_util{$mode};
		}
		$sar{cpu}{"all_cpu_busy"}{$cpulabel}{$timestamp_ms} = $busy;
		next;
	}

	# 03:25:51 AM    proc/s   cswch/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+proc\/s\s+cswch\/s$/ )) {
		$mode = "proc_cswch";
		next;
	}
	# 03:25:56 AM      0.80    352.20
	if (( $mode eq "proc_cswch" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+.\d+)$/ )) {
		$time = $1;
		my %proc_ctx = ( 'ProcessesCreatedSec' => $2, 'ContextSwitchesSec' => $3 );
		$timestamp_ms = calc_timestamp($time);
		$sar{system}{proc_cswch_sec}{processes_created_sec}{$timestamp_ms} = $proc_ctx{'ProcessesCreatedSec'};
		$sar{system}{proc_cswch_sec}{context_switches_sec}{$timestamp_ms} = $proc_ctx{'ContextSwitchesSec'};
		next;
	}

	# 03:25:51 AM      INTR    intr/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /\d+\:\d+\:\d+(\s\w\w)?\s+INTR\s+intr\/s$/ )) {
		$mode = "intr";
		next;
	}
	# 03:25:56 AM       sum    294.60
	# 03:25:56 AM         0      0.00
	if (( $mode eq "intr" ) &&
	    ( $line =~ /($tsre)\s+sum\s+(\d+.\d+)$/ )) {
		$time = $1;
		my $intr_sec = $2;
		$timestamp_ms = calc_timestamp($time);
		$sar{system}{interrupts_sec}{all_interrupts}{$timestamp_ms} = $intr_sec;
		next;
	}

	# 03:25:51 AM  pswpin/s pswpout/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+pswpin\/s\s+pswpout\/s$/ )) {
		$mode = "pageswap";
		next;
	}
	# 03:25:56 AM      0.00      0.00
	if (( $mode eq "pageswap" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+.\d+)$/ )) {
		$time = $1;
		my %pswp_sec = ( 'In' => $2, 'Out' => $3 );
		$timestamp_ms = calc_timestamp($time);
		$sar{memory}{memory_activity}{page_swaps_in_sec}{$timestamp_ms} = $pswp_sec{'In'};
		$sar{memory}{memory_activity}{page_swaps_out_sec}{$timestamp_ms} = $pswp_sec{'Out'};
		next;
	}

	# 03:25:51 AM  pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+pgpgin\/s\s+pgpgout\/s\s+fault\/s\s+majflt\/s\s+pgfree\/s\s+pgscank\/s\s+pgscand\/s\s+pgsteal\/s\s+%vmeff$/ )) {
		$mode = "pagefault";
		next;
	}
	# 03:25:56 AM      0.00      1.60     29.60      0.00     76.00      0.00      0.00      0.00      0.00
	if (( $mode eq "pagefault" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %disk_pg_kb_sec = ( 'In' => $2, 'Out' => $3);
		my %fault_sec = ( 'Minor' => $4 - $5, 'Major' => $5 );
		my %other_pg_sec = ( 'Free' => $6, 'KswapdSacn' => $7, 'Scan' => $8, 'Steal' => $9 );
		my $vmeff_pct = $10;
		$timestamp_ms = calc_timestamp($time);
		$sar{memory}{memory_activity}{pgpgin_sec}{$timestamp_ms} = $disk_pg_kb_sec{'In'};
		$sar{memory}{memory_activity}{pgpgout_sec}{$timestamp_ms} = $disk_pg_kb_sec{'Out'};
		$sar{memory}{memory_activity}{fault_sec}{$timestamp_ms} = sprintf("%.2f", $fault_sec{'Minor'} + $fault_sec{'Major'});
		$sar{memory}{memory_activity}{majflt_sec}{$timestamp_ms} = $fault_sec{'Major'};
		$sar{memory}{memory_activity}{pgfree_sec}{$timestamp_ms} = $other_pg_sec{'Free'};
		$sar{memory}{memory_activity}{pgscank_sec}{$timestamp_ms} = $other_pg_sec{'KswapdSacn'};
		$sar{memory}{memory_activity}{pgscand_sec}{$timestamp_ms} = $other_pg_sec{'Scan'};
		$sar{memory}{memory_activity}{pgsteal_sec}{$timestamp_ms} = $other_pg_sec{'Steal'};
		$sar{memory}{memory_activity}{vmeff_pct}{$timestamp_ms} = $vmeff_pct;
		next;
	}

	# 03:25:51 AM       tps      rtps      wtps   bread/s   bwrtn/s
	# 03:25:56 AM      0.40      0.00      0.40      0.00      3.20

	# 03:25:51 AM kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty  kbanonpg    kbslab  kbkstack   kbpgtbl  kbvmused
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+kbmemfree\s+kbavail\s+kbmemused\s+%memused\s+kbbuffers\s+kbcached\s+kbcommit\s+%commit\s+kbactive\s+kbinact\s+kbdirty\s+kbanonpg\s+kbslab\s+kbkstack\s+kbpgtbl\s+kbvmused$/ )) {
		$mode = "mem_usage";
		next;
	}
	# 03:25:56 AM  24045600  63360192   1186124      1.80      2324  26717516   1877828      2.85   9057276  17947280       640    309160  13842856      9360     20588    201476
	if (( $mode eq "mem_usage" ) &&
	    ( $line =~ /($tsre)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\.\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\.\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/ )) {
		$time = $1;
		my %kbmem_system = ( 'Free' => $2, 'Buffers' => $6, 'Cached' => $7, 'Slab' => $14 );
		my %kbmem_activeness = ( 'Active' => $10, 'Inactive' => $11 );
		my %kbmem_kernel = ( 'Dirty' => $12, 'Stack' => $15, 'NoPage' => $13, 'PageTable' => $16 );
		my %kbmem_other = ( 'Avail' => $3, 'Used' => $4, 'Commit' => $8, 'VmUsed' => $17 );
		my %pctmem = ( 'Used' => $5, 'Commit' => $9 ) ;
		my %memusage = ( 'SystemMemUsageKB' => \%kbmem_system, 'SystemMemActivenessKB' => \%kbmem_activeness,
				'KernelMemUsageKB' => \%kbmem_kernel, 'OtherMemUsageKB' => \%kbmem_other, 'PercentMemUsageKB' => \%pctmem );
		$timestamp_ms = calc_timestamp($time);
		$sar{memory}{memory_usage}{kbmemfree}{$timestamp_ms} = $kbmem_system{'Free'};
		$sar{memory}{memory_usage}{kbavail}{$timestamp_ms} = $kbmem_other{'Avail'};
		$sar{memory}{memory_usage}{kbmemused}{$timestamp_ms} = $kbmem_other{'Used'};
		$sar{memory}{memory_usage}{memused_pct}{$timestamp_ms} = $pctmem{'Used'};
		$sar{memory}{memory_usage}{kbbuffers}{$timestamp_ms} = $kbmem_system{'Buffers'};
		$sar{memory}{memory_usage}{kbcached}{$timestamp_ms} = $kbmem_system{'Cached'};
		$sar{memory}{memory_usage}{kbcommit}{$timestamp_ms} = $kbmem_other{'Commit'};
		$sar{memory}{memory_usage}{commit_pct}{$timestamp_ms} = $pctmem{'Commit'};
		$sar{memory}{memory_usage}{kbactive}{$timestamp_ms} = $kbmem_activeness{'Active'};
		$sar{memory}{memory_usage}{kbinactive}{$timestamp_ms} = $kbmem_activeness{'Inactive'};
		$sar{memory}{memory_usage}{kbdirty}{$timestamp_ms} = $kbmem_kernel{'Dirty'};
		$sar{memory}{memory_usage}{kbanonpg}{$timestamp_ms} = $kbmem_kernel{'NoPage'};
		$sar{memory}{memory_usage}{kbslab}{$timestamp_ms} = $kbmem_system{'Slab'};
		$sar{memory}{memory_usage}{kbkstack}{$timestamp_ms} = $kbmem_kernel{'Stack'};
		$sar{memory}{memory_usage}{kbpgtbl}{$timestamp_ms} = $kbmem_kernel{'PageTable'};
		$sar{memory}{memory_usage}{kbvmused}{$timestamp_ms} = $kbmem_other{'VmUsed'};
		next;
	}

	# 03:25:51 AM kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+kbswpfree\s+kbswpused\s+%swpused\s+kbswpcad\s+%swpcad$/ )) {
		$mode = "swap_usage";
		next;
	}
	# 03:25:56 AM         0         0      0.00         0      0.00
	if (( $mode eq "swap_usage" ) &&
	    ( $line =~ /($tsre)\s+(\d+)\s+(\d+)\s+(\d+\.\d+)\s+(\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %kbswap = ( 'Used' => $3, 'Free' => $2, 'Cached' => $5 );
		my %pctswap = ( 'Used' => $4, 'Cached' => $6 );
		$timestamp_ms = calc_timestamp($time);
		$sar{memory}{memory_usage}{kbswpfree}{$timestamp_ms} = $kbswap{'Free'};
		$sar{memory}{memory_usage}{kbswpused}{$timestamp_ms} = $kbswap{'Used'};
		$sar{memory}{memory_usage}{swpused_pct}{$timestamp_ms} = $pctswap{'Used'};
		$sar{memory}{memory_usage}{kbswpcad}{$timestamp_ms} = $kbswap{'Cached'};
		$sar{memory}{memory_usage}{swpcad_pct}{$timestamp_ms} = $pctswap{'Cached'};
		next;
	}

	# 03:25:51 AM kbhugfree kbhugused  %hugused
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+kbhugfree\s+kbhugused\s+%hugused$/ )) {
		$mode = "hugemem_usage";
		next;
	}
	# 03:25:56 AM         0         0      0.00
	if (( $mode eq "hugemem_usage" ) &&
	    ( $line =~ /($tsre)\s+(\d+)\s+(\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %kbhuge = ( 'Free' => $2, 'Used' => $3 );
		my %pcthuge = ( 'Used' => $4 );
		my %hugemem = ( 'HugePageUsageKB' => \%kbhuge, 'HugePageUsagePercent' => \%pcthuge );
		$timestamp_ms = calc_timestamp($time);
		$sar{memory}{memory_usage}{kbhugefree}{$timestamp_ms} = $kbhuge{'Free'};
		$sar{memory}{memory_usage}{kbhugeused}{$timestamp_ms} = $kbhuge{'Used'};
		$sar{memory}{memory_usage}{hugeused_pct}{$timestamp_ms} = $pcthuge{'Used'};
		next;
	}

	# 03:25:51 AM dentunusd   file-nr  inode-nr    pty-nr
	# 03:25:56 AM  41593563      4752   4300862         2

	# 03:25:51 AM   runq-sz  plist-sz   ldavg-1   ldavg-5  ldavg-15   blocked
	# 03:25:56 AM         0       555      0.25      0.14      0.14         0

	# 03:25:51 AM       TTY   rcvin/s  txmtin/s framerr/s prtyerr/s     brk/s   ovrun/s
	# 03:25:56 AM         0      0.00      0.00      0.00      0.00      0.00      0.00
	# 03:25:56 AM         1      0.00      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM       DEV       tps     rkB/s     wkB/s   areq-sz    aqu-sz     await     svctm     %util
	# 03:25:56 AM    dev8-0      0.40      0.00      1.60      4.00      0.00      0.00      0.00      0.00
	# 03:25:56 AM   dev8-16      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	# 03:25:56 AM   dev8-32      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	# 03:25:56 AM  dev253-0      0.40      0.00      1.60      4.00      0.00      0.00      0.00      0.00
	# 03:25:56 AM  dev253-1      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+IFACE\s+rxpck\/s\s+txpck\/s\s+rxkB\/s\s+txkB\/s\s+rxcmp\/s\s+txcmp\/s\s+rxmcst\/s\s+%ifutil$/ )) {
		$mode = "net_dev";
		next;
	}
	# 03:25:56 AM  enp3s0f0     21.80     20.80     18.20      2.44      0.00      0.00      0.00      0.01
	if (( $mode eq "net_dev" ) &&
	    ( $line =~ /($tsre)\s+(\S+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my $interface = $2;
		if ( $interface eq ";vdsmdummy;" ) {
			next;
		}
		my %packets_sec = ( 'rx' => $3, 'tx' => $4 );
		my %mbits_sec = ( 'rx' => $5 * 1024 * 8 / 1000000, 'tx' => $6 * 1024 * 8 / 1000000 );
		my %comp_packets_sec = ( 'rx' => $7, 'tx' => $8 );
		my %mult_packets_sec = ( 'rx' => $9 );
		my %network_sec = ( 'NetdevPacketsSec' => \%packets_sec, 'NetdevLayer1MbitSec' => \%mbits_sec,
					'NetdevCompressedPacketsSec' => \%comp_packets_sec, 'NetdevMulticastPacketsSec' => \%mult_packets_sec );
		my $pct_util = $10;
		my $timestamp_ms = calc_timestamp($time);
		$sar{network_l2}{network_packets_sec}{"$interface" . "-rx"}{$timestamp_ms} = $packets_sec{'rx'};
		$sar{network_l2}{network_packets_sec}{"$interface" . "-tx"}{$timestamp_ms} = $packets_sec{'tx'};
		$sar{network_l2}{network_compressed_packets_sec}{"$interface" . "-rx"}{$timestamp_ms} = $comp_packets_sec{'rx'};
		$sar{network_l2}{network_compressed_packets_sec}{"$interface" . "-tx"}{$timestamp_ms} = $comp_packets_sec{'tx'};
		$sar{network_l2}{network_multicast_packets_sec}{"$interface" . "-rx"}{$timestamp_ms} = $mult_packets_sec{'rx'};
		$sar{network_l2}{network_Mbits_sec}{"$interface" . "-rx"}{$timestamp_ms} = $mbits_sec{'rx'};
		$sar{network_l2}{network_Mbits_sec}{"$interface" . "-tx"}{$timestamp_ms} = $mbits_sec{'tx'};
		next;
	}

	# 03:25:51 AM     IFACE   rxerr/s   txerr/s    coll/s  rxdrop/s  txdrop/s  txcarr/s  rxfram/s  rxfifo/s  txfifo/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+IFACE\s+rxerr\/s\s+txerr\/s\s+coll\/s\s+rxdrop\/s\s+txdrop\/s\s+txcarr\/s\s+rxfram\/s\s+rxfifo\/s\s+txfifo\/s$/ )) {
		$mode = "net_dev_error";
		next;
	}
	# 03:25:56 AM  enp3s0f0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	if (( $mode eq "net_dev_error" ) &&
	    ( $line =~ /($tsre)\s+(\S+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my $interface = $2;
		if ( $interface eq ";vdsmdummy;" ) {
			next;
		}
		my $rx_err_sec = $3;
		my $tx_err_sec = $4;
		my %err_tx_packets_sec = ( 'Collision' => $5, 'Drop' => $7, 'Carrier' => $8, 'Fifo' => $11 );
		my %err_rx_packets_sec = ( 'Drop' => $6, 'Fifo' => $11, 'Frame' => $9 );
		my %err_packets_sec = ( 'tx' => \%err_tx_packets_sec, 'rx' => \%err_rx_packets_sec );
		$timestamp_ms = calc_timestamp($time);
		$sar{network_l2}{errors_sec}{"$interface" . "-rx"}{$timestamp_ms} = $rx_err_sec;
		$sar{network_l2}{errors_sec}{"$interface" . "-tx"}{$timestamp_ms} = $tx_err_sec;
		$sar{network_l2}{drops_sec}{"$interface" . "-rx"}{$timestamp_ms} = $err_packets_sec{'rx'}{'Drop'};
		$sar{network_l2}{drops_sec}{"$interface" . "-tx"}{$timestamp_ms} = $err_packets_sec{'tx'}{'Drop'};
		$sar{network_l2}{carrier_errors}{"$interface"}{$timestamp_ms} = $err_packets_sec{'tx'}{'Carrier'};
		$sar{network_l2}{frame_alignment_errors}{"$interface"}{$timestamp_ms} = $err_packets_sec{'rx'}{'Frame'};
		$sar{network_l2}{fifo_overrun_errors}{"$interface" . "-rx"}{$timestamp_ms} = $err_packets_sec{'rx'}{'Fifo'};
		$sar{network_l2}{fifo_overrun_errors}{"$interface" . "-tx"}{$timestamp_ms} = $err_packets_sec{'tx'}{'Fifo'};
		next;
	}

	# 03:25:51 AM    call/s retrans/s    read/s   write/s  access/s  getatt/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+call\/s\s+retrans\/s\s+read\/s\s+write\/s\s+access\/s\s+getatt\/s$/ )) {
		$mode = "net_nfs_client";
		next;
	}
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00
	if (( $mode eq "net_nfs_client") &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my $calls_sec = $2;
		my %nfs_client_calls_sec = ( 'Retrans' => $3, 'Read' => $4, 'Write' => $5, 'Access' => $6, 'GetAttr' => $7 );
		my $timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{nfs_client}{calls_sec}{$timestamp_ms} = $calls_sec;
		$sar{network_l345}{nfs_client}{reads_sec}{$timestamp_ms} = $nfs_client_calls_sec{'Read'};
		$sar{network_l345}{nfs_client}{writes_sec}{$timestamp_ms} = $nfs_client_calls_sec{'Write'};
		$sar{network_l345}{nfs_client}{access_sec}{$timestamp_ms} = $nfs_client_calls_sec{'Access'};
		$sar{network_l345}{nfs_client}{getattr_sec}{$timestamp_ms} = $nfs_client_calls_sec{'GetAttr'};
		next;
	}

	# 03:25:51 AM   scall/s badcall/s  packet/s     udp/s     tcp/s     hit/s    miss/s   sread/s  swrite/s saccess/s sgetatt/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+scall\/s\s+badcall\/s\s+packet\/s\s+udp\/s\s+tcp\/s\s+hit\/s\s+miss\/s\s+sread\/s\s+swrite\/s\s+saccess\/s\s+sgetatt\/s$/ )) {
		$mode = "net_nfs_server";
		next;
	}
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	if (( $mode eq "net_nfs_server" ) &&
	    ( $line =~ /(\d+\:\d+\:\d+\s)\w\w\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %nfs_server_goodbad_sec = ( 'Bad' => $3, 'Good' => $2 - $6 );
		my %nfs_server_proto_sec = ( 'Udp' => $5, 'Tcp' => $6 );
		my %nfs_server_hitmiss_sec = ( 'Hit' => $7, 'Miss' => $8 );
		my %nfs_server_calls_sec = ( 'Read' => $9, 'Write' => $10, 'Access' => $11, 'GetAttr' => $12 );
		my $timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{nfs_server}{calls_sec}{$timestamp_ms} = sprintf("%.2f", $nfs_server_goodbad_sec{'Good'} + $nfs_server_goodbad_sec{'Bad'});
		$sar{network_l345}{nfs_server}{badcalls_sec}{$timestamp_ms} = $nfs_server_goodbad_sec{'Bad'};
		$sar{network_l345}{nfs_server}{rx_udp_packets_sec}{$timestamp_ms} = $nfs_server_proto_sec{'Udp'};
		$sar{network_l345}{nfs_server}{rx_tcp_packets_sec}{$timestamp_ms} = $nfs_server_proto_sec{'Tcp'};
		$sar{network_l345}{nfs_server}{reply_cache_hits_sec}{$timestamp_ms} = $nfs_server_hitmiss_sec{'Hit'};
		$sar{network_l345}{nfs_server}{reply_cache_misses_sec}{$timestamp_ms} = $nfs_server_hitmiss_sec{'Miss'};
		$sar{network_l345}{nfs_server}{rpc_read_calls_sec}{$timestamp_ms} = $nfs_server_calls_sec{'Read'};
		$sar{network_l345}{nfs_server}{rpc_write_calls_sec}{$timestamp_ms} = $nfs_server_calls_sec{'Write'};
		$sar{network_l345}{nfs_server}{rpc_access_calls_sec}{$timestamp_ms} = $nfs_server_calls_sec{'Access'};
		$sar{network_l345}{nfs_server}{rpc_getattr_calls_sec}{$timestamp_ms} = $nfs_server_calls_sec{'GetAttr'};
		next;
	}

	# 03:25:51 AM    totsck    tcpsck    udpsck    rawsck   ip-frag    tcp-tw
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+totsck\s+tcpsck\s+udpsck\s+rawsck\s+ip-frag\s+tcp-tw$/ )) {
		$mode = "net_sock";
		next;
	}
	# 03:25:56 AM       495       101         4         0         0         0
	if (( $mode eq "net_sock" ) &&
	    ( $line =~ /($tsre)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/ )) {
		$time = $1;
		my $totsck = $2;
		my %sck = ( 'Tcp' => $3, 'Udp' => $4, 'Raw' => $5 );
		my $tcpsck = $3;
		my $udpsck = $4;
		my $rawsck = $5;
		my $ipfrag = $6;
		my $tcptw = $7;
		$timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{sockets}{total_sockets}{$timestamp_ms} = $totsck;
		$sar{network_l345}{sockets}{tcp_sockets}{$timestamp_ms} = $sck{'Tcp'};
		$sar{network_l345}{sockets}{udp_sockets}{$timestamp_ms} = $sck{'Udp'};
		$sar{network_l345}{sockets}{raw_sockets}{$timestamp_ms} = $sck{'Raw'};
		$sar{network_l345}{sockets}{raw_sockets}{$timestamp_ms} = $ipfrag;
		$sar{network_l345}{sockets}{tcp_timewait_sockets}{$timestamp_ms} = $tcptw;
		next;
	}

	# 03:25:51 AM    irec/s  fwddgm/s    idel/s     orq/s   asmrq/s   asmok/s  fragok/s fragcrt/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+irec\/s\s+fwddgm\/s\s+idel\/s\s+orq\/s\s+asmrq\/s\s+asmok\/s\s+fragok\/s\s+fragcrt\/s$/ )) {
		$mode = "net_ip";
		next;
	}
	# 03:25:56 AM      8.20      0.00      7.80     18.40      0.00      0.00      0.00      0.00
	if (( $mode eq "net_ip" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %net_ip = ( 'RxDatagramsSec' => $2,  # The total number of input datagrams received from interfaces per second, including those received in error [ipInReceives].
				'FwdDatagramsSec' => $3,  # The  number  of input datagrams per second, for which this entity was not their final IP destination, as a result
				  # of which an attempt was made to find a route to forward them to that final destination [ipForwDatagrams].
				'DelDatagramsSec' => $4, # The total number of input datagrams successfully delivered per second to IP user-protocols (including ICMP) [ipInDelivers].
				'OutputReqDatagramsSec' => $5, # The total number of IP datagrams which local IP user-protocols (including ICMP) supplied per second to IP in requests
			       # for transmission [ipOutRequests].  Note that  this  counter  does  not include any datagrams counted in fwddgm/s.
				'RxNeedReassmbleDatagramsSec' => $6, # The number of IP fragments received per second which needed to be reassembled at this entity [ipReasmReqds].
				'ReassembledDatagramsSec' => $7, # The number of IP datagrams successfully re-assembled per second [ipReasmOKs].
				'FragmentedDatagramsSec' => $8, # The number of IP datagrams that have been successfully fragmented at this entity per second [ipFragOKs].
				'FragmentCreatedSec' => $9 );# The number of IP datagram fragments that have been generated per second as a result of fragmentation at this entity [ipFragCreates].
		my $timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{ip}{rx_datagrams_sec}{$timestamp_ms} = $net_ip{'RxDatagramsSec'};
		$sar{network_l345}{ip}{rx_datagrams_forwarded_sec}{$timestamp_ms} = $net_ip{'FwdDatagramsSec'};
		$sar{network_l345}{ip}{datagrams_delivered_sec}{$timestamp_ms} = $net_ip{'DelDatagramsSec'};
		$sar{network_l345}{ip}{datagrams_output_requests_sec}{$timestamp_ms} = $net_ip{'OutputReqDatagramsSec'};
		$sar{network_l345}{ip}{fragments_rx_need_reassemble_sec}{$timestamp_ms} = $net_ip{'RxNeedReassmbleDatagramsSec'};
		$sar{network_l345}{ip}{datagrams_reassmbled_sec}{$timestamp_ms} = $net_ip{'ReassembledDatagramsSec'};
		$sar{network_l345}{ip}{datagrams_fragmented_ok_sec}{$timestamp_ms} = $net_ip{'FragmentedDatagramsSec'};
		$sar{network_l345}{ip}{datagram_fragments_gen_sec}{$timestamp_ms} = $net_ip{'FragmentCreatedSec'};
		next;
	}

	# 03:25:51 AM ihdrerr/s iadrerr/s iukwnpr/s   idisc/s   odisc/s   onort/s    asmf/s   fragf/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+ihdrerr\/s\s+iadrerr\/s\s+iukwnpr\/s\s+idisc\/s\s+odisc\/s\s+onort\/s\s+asmf\/s\s+fragf\/s$/ )) {
		$mode = "net_ip_error";
		next;
	}
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	if (( $mode eq "net_ip_error" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %ipv4_rx_discarded_sec = ( 'HeaderError' => $3, 'DestIpError' => $3, 'UnknownProto' => $4, 'NoBuff' => $5, 'ReassemblyFail' => $8 );
		my %ipv4_tx_discarded_sec = ( 'NoRoute' => $7, 'FragmentFail' => $9, 'NoBuff' => $6 );
		my %ipv4_discarded_sec = ( 'Rx' => \%ipv4_rx_discarded_sec, 'Tx' => \%ipv4_tx_discarded_sec );
		my $timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{ip}{rx_datagrams_header_err_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Rx'}{'HeaderError'};
		$sar{network_l345}{ip}{rx_datagrams_invalid_addr_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Rx'}{'DestIpError'};
		$sar{network_l345}{ip}{rx_datagrams_unknown_proto_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Rx'}{'UnknownProto'};
		$sar{network_l345}{ip}{rx_datagrams_nobuff_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Rx'}{'NoBuff'};
		$sar{network_l345}{ip}{tx_datagrams_nobuff_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Tx'}{'NoBuff'};
		$sar{network_l345}{ip}{tx_datagrams_noroute_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Tx'}{'NoRoute'};
		$sar{network_l345}{ip}{rx_datagrams_reassmbly_fail_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Rx'}{'ReassemblyFail'};
		$sar{network_l345}{ip}{tx_datagrams_fragment_fail_sec}{$timestamp_ms} = $ipv4_discarded_sec{'Tx'}{'FragmentFail'};
		next;
	}

	# 03:25:51 AM    imsg/s    omsg/s    iech/s   iechr/s    oech/s   oechr/s     itm/s    itmr/s     otm/s    otmr/s  iadrmk/s iadrmkr/s  oadrmk/s oadrmkr/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+imsg\/s\s+omsg\/s\s+iech\/s\s+iechr\/s\s+oech\/s\s+oechr\/s\s+itm\/s\s+itmr\/s\s+otm\/s\s+otmr\/s\s+iadrmk\/s\s+iadrmkr\/s\s+oadrmk\/s\s+oadrmkr\/s$/ )) {
		$mode = "net_icmp";
		next;
	}
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	if (( $mode eq "net_icmp" ) &&
	    ( $line =~ /(\d+\:\d+\:\d+\s)\w\w\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my $imsg_sec = $2; # The total number of ICMP messages which the entity received per second [icmpInMsgs].
		my $omsg_sec = $3; # The total number of ICMP messages which this entity attempted to send per second [icmpOutMsgs].
		my %icmp_rx_req_sec = ( 'Echo' => $4, 'Timestamp' => $8, 'AddrMask' => $12 );
		my %icmp_rx_reply_sec = ( 'Echo' => $5, 'Timestamp' => $9, 'AddrMask' => $13 );
		my %icmp_tx_req_sec = ( 'Echo' => $6, 'Timestamp' => $10, 'AddrMask' => $14 );
		my %icmp_tx_reply_sec = ( 'Echo' => $7, 'Timestamp' => $11, 'AddrMask' => $15 );
		my %icmp_sec;
		$icmp_sec{'Rx'}{'Request'} = \%icmp_rx_req_sec;
		$icmp_sec{'Rx'}{'Reply'} = \%icmp_rx_reply_sec;
		$icmp_sec{'Tx'}{'Request'} = \%icmp_tx_req_sec;
		$icmp_sec{'Tx'}{'Reply'} = \%icmp_tx_reply_sec;
		$timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{icmp}{rx_messages_sec}{$timestamp_ms} = $imsg_sec;
		$sar{network_l345}{icmp}{tx_messages_sec}{$timestamp_ms} = $omsg_sec;
		$sar{network_l345}{icmp}{rx_echo_request_messages_sec}{$timestamp_ms} = $icmp_sec{'Rx'}{'Request'}{'Echo'};
		$sar{network_l345}{icmp}{rx_echo_reply_messages_sec}{$timestamp_ms} = $icmp_sec{'Rx'}{'Reply'}{'Echo'};
		$sar{network_l345}{icmp}{tx_echo_request_messages_sec}{$timestamp_ms} = $icmp_sec{'Tx'}{'Request'}{'Echo'};
		$sar{network_l345}{icmp}{tx_echo_reply_messages_sec}{$timestamp_ms} = $icmp_sec{'Tx'}{'Reply'}{'Echo'};
		$sar{network_l345}{icmp}{rx_timestamp_request_messages_sec}{$timestamp_ms} = $icmp_sec{'Rx'}{'Request'}{'Timestamp'};
		$sar{network_l345}{icmp}{rx_timestamp_reply_messages_sec}{$timestamp_ms} = $icmp_sec{'Rx'}{'Reply'}{'Timestamp'};
		$sar{network_l345}{icmp}{tx_timestamp_request_messages_sec}{$timestamp_ms} = $icmp_sec{'Tx'}{'Request'}{'Timestamp'};
		$sar{network_l345}{icmp}{tx_timestamp_reply_messages_sec}{$timestamp_ms} = $icmp_sec{'Tx'}{'Reply'}{'Timestamp'};
		$sar{network_l345}{icmp}{rx_addrmask_request_messages_sec}{$timestamp_ms} = $icmp_sec{'Rx'}{'Request'}{'AddrMask'};
		$sar{network_l345}{icmp}{rx_addrmask_reply_messages_sec}{$timestamp_ms} = $icmp_sec{'Rx'}{'Reply'}{'AddrMask'};
		$sar{network_l345}{icmp}{tx_addrmask_request_messages_sec}{$timestamp_ms} = $icmp_sec{'Tx'}{'Request'}{'AddrMask'};
		$sar{network_l345}{icmp}{tx_addrmask_reply_messages_sec}{$timestamp_ms} = $icmp_sec{'Tx'}{'Request'}{'AddrMask'};
		next;
	}

	# 03:25:51 AM    ierr/s    oerr/s idstunr/s odstunr/s   itmex/s   otmex/s iparmpb/s oparmpb/s   isrcq/s   osrcq/s  iredir/s  oredir/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+ierr\/s\s+oerr\/s\s+idstunr\/s\s+odstunr\/s\s+itmex\/s\s+otmex\/s\s+iparmpb\/s\s+oparmpb\/s\s+isrcq\/s\s+osrcq\/s\s+iredir\/s\s+oredir\/s$/ )) {
		$mode = "net_eicmp";
		next;
	}
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
	if (( $mode eq "net_eicmp" ) &&
	    ( $line =~ /(\d+\:\d+\:\d+\s)\w\w\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %icmperr_rx_sec = ( 'IcmpError' => $2, 'DestUnreachable' => $4, 'TimeExceeded' => $6, 'ParamProblem' => $8, 'SourceQuench' => $10, 'Redirect' => $12 );
		my %icmperr_tx_sec = ( 'IcmpError' => $3, 'DestUnreachable' => $5, 'TimeExceeded' => $7, 'ParamProblem' => $9, 'SourceQuench' => $11, 'Redirect' => $13 );
		my %icmperr_sec;
		$icmperr_sec{'Rx'} = \%icmperr_rx_sec;
		$icmperr_sec{'Tx'} = \%icmperr_tx_sec;
		$timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{icmp}{rx_icmp_err_sec}{$timestamp_ms} = $icmperr_sec{'Rx'}{'IcmpError'};
		$sar{network_l345}{icmp}{tx_icmp_err_sec}{$timestamp_ms} = $icmperr_sec{'Tx'}{'IcmpError'};
		$sar{network_l345}{icmp}{rx_dest_unreachable_sec}{$timestamp_ms} = $icmperr_sec{'Rx'}{'DestUnreachable'};
		$sar{network_l345}{icmp}{tx_dest_unreachable_sec}{$timestamp_ms} = $icmperr_sec{'Tx'}{'DestUnreachable'};
		$sar{network_l345}{icmp}{rx_time_exceed_sec}{$timestamp_ms} = $icmperr_sec{'Rx'}{'TimeExceeded'};
		$sar{network_l345}{icmp}{tx_time_exceed_sec}{$timestamp_ms} = $icmperr_sec{'Tx'}{'TimeExceeded'};
		$sar{network_l345}{icmp}{rx_param_prob_sec}{$timestamp_ms} = $icmperr_sec{'Rx'}{'ParamProblem'};
		$sar{network_l345}{icmp}{tx_param_prob_sec}{$timestamp_ms} = $icmperr_sec{'Tx'}{'ParamProblem'};
		$sar{network_l345}{icmp}{rx_src_quench_sec}{$timestamp_ms} = $icmperr_sec{'Rx'}{'SourceQuench'};
		$sar{network_l345}{icmp}{tx_src_quench_sec}{$timestamp_ms} = $icmperr_sec{'Tx'}{'SourceQuench'};
		$sar{network_l345}{icmp}{rx_redir_sec}{$timestamp_ms} = $icmperr_sec{'Rx'}{'Redirect'};
		$sar{network_l345}{icmp}{tx_redir_sec}{$timestamp_ms} = $icmperr_sec{'Tx'}{'Redirect'};
		next;
	}

	# 03:25:51 AM  active/s passive/s    iseg/s    oseg/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+active\/s\s+passive\/s\s+iseg\/s\s+oseg\/s$/ )) {
		$mode = "net_tcp";
		next;
	}
	# 03:25:56 AM      3.60      0.00      0.20      3.60
	if (( $mode eq "net_tcp" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %tcp_opens_sec = ( 'Active' => $2, 'Passive' => $3 );
		my %tcp_segments_sec = ( 'Input' => $4, 'Output' => $5 );
		$timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{tcp_sockets}{active_opens_sec}{$timestamp_ms} = $tcp_opens_sec{'Active'};
		$sar{network_l345}{tcp_sockets}{passive_opens_sec}{$timestamp_ms} = $tcp_opens_sec{'Passive'};
		$sar{network_l345}{tcp_sockets}{rx_segments_sec}{$timestamp_ms} = $tcp_segments_sec{'Input'};
		$sar{network_l345}{tcp_sockets}{tx_segments_sec}{$timestamp_ms} = $tcp_segments_sec{'Output'};
		next;
	}

	# 03:25:51 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+atmptf\/s\s+estres\/s\s+retrans\/s\s+isegerr\/s\s+orsts\/s$/ )) {
		$mode = "net_tcp_error";
		next;
	}
	# 03:25:56 AM      0.00      0.00      7.20      0.00      0.00
	if (( $mode eq "net_tcp_error" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %tcperr_closes_sec = ( 'AttemptFail' => $2, 'EstablishReset' => $3 );
		my $tcp_retrans_sec = $4;
		my $tcp_input_segerr_sec = $5;
		my $tcp_output_rsts_sec = $5; # The number of TCP segments sent per second containing the RST flag [tcpOutRsts].
		$timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{tcp_sockets}{tcp_attempt_fails_sec}{$timestamp_ms} = $tcperr_closes_sec{'AttemptFail'};
		$sar{network_l345}{tcp_sockets}{tcp_establish_resets_sec}{$timestamp_ms} = $tcperr_closes_sec{'EstablishReset'};
		$sar{network_l345}{tcp_sockets}{tcp_segments_retransmitted_sec}{$timestamp_ms} = $tcp_retrans_sec;
		$sar{network_l345}{tcp_sockets}{tcp_rx_segments_error_sec}{$timestamp_ms} = $tcp_input_segerr_sec;
		$sar{network_l345}{tcp_sockets}{tcp_tx_segments_RSTflag_sec}{$timestamp_ms} = $tcp_output_rsts_sec;
		next;
	}

	# 03:25:51 AM    idgm/s    odgm/s  noport/s idgmerr/s
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+idgm\/s\s+odgm\/s\s+noport\/s\s+idgmerr\/s$/ )) {
		$mode = "net_udp";
		next;
	}
	# 03:25:56 AM      7.60      7.60      0.00      0.00
	if (( $mode eq "net_udp" ) &&
	    ( $line =~ /($tsre)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)$/ )) {
		$time = $1;
		my %udp_datagrams_sec = ( 'Rx' => $2, 'Tx' => $3 );
		my %udp_error_datagrams_sec = ( 'NoPort' => $4, 'Other' => $5 );
		my $noport_sec = $4; # The total number of received UDP datagrams per second for which there was no application at the destination port [udpNoPorts].
		my $idgmerr_sec = $5; # The number of received UDP datagrams per second that could not be delivered for reasons other than the lack of an application at the destination port [udpInErrors].
		my $timestamp_ms = calc_timestamp($time);
		$sar{network_l345}{udp}{rx_datagrams_sec}{$timestamp_ms} = $udp_datagrams_sec{'Rx'};
		$sar{network_l345}{udp}{tx_datagrams_sec}{$timestamp_ms} = $udp_datagrams_sec{'Tx'};
		$sar{network_l345}{udp}{rx_datagrams_discard_no_dest_port_sec}{$timestamp_ms} = $udp_error_datagrams_sec{'NoPort'};
		$sar{network_l345}{udp}{rx_datagrams_discard_other_error}{$timestamp_ms} = $udp_error_datagrams_sec{'Other'};
		next;
	}

	# 03:25:51 AM   tcp6sck   udp6sck   raw6sck  ip6-frag
	# 03:25:56 AM         8         2         1         0

	# 03:25:51 AM   irec6/s fwddgm6/s   idel6/s    orq6/s  asmrq6/s  asmok6/s imcpck6/s omcpck6/s fragok6/s fragcr6/s
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM ihdrer6/s iadrer6/s iukwnp6/s  i2big6/s  idisc6/s  odisc6/s  inort6/s  onort6/s   asmf6/s  fragf6/s itrpck6/s
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM   imsg6/s   omsg6/s   iech6/s  iechr6/s  oechr6/s  igmbq6/s  igmbr6/s  ogmbr6/s igmbrd6/s ogmbrd6/s irtsol6/s ortsol6/s  irtad6/s inbsol6/s onbsol6/s  inbad6/s  onbad6/s
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM   ierr6/s idtunr6/s odtunr6/s  itmex6/s  otmex6/s iprmpb6/s oprmpb6/s iredir6/s oredir6/s ipck2b6/s opck2b6/s
	# 03:25:56 AM      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM   idgm6/s   odgm6/s noport6/s idgmer6/s
	# 03:25:56 AM      0.00      0.00      0.00      0.00

	# 03:25:51 AM     CPU   total/s   dropd/s squeezd/s  rx_rps/s flw_lim/s
	# 03:25:56 AM     all     10.20      0.00      0.00      0.00      0.00
	# 03:25:56 AM       0      0.00      0.00      0.00      0.00      0.00

	# 03:25:51 AM     CPU       MHz
	if (( $mode eq "none" ) &&
	    ( $line =~ /$tsre\s+CPU\s+MHz$/ )) {
		$mode = "cpu_freq";
		next;
	}
	# 03:25:56 AM     all   2793.00
	# 03:25:56 AM       0   2793.00
	if (( $mode eq "cpu_freq" ) && ( $line =~ /($tsre)\s+(\d+)\s+(\d+.\d+)$/ )) {
		$line =~ s/($tsre)\s+//;
		$time = $1;
		(my $cpuid, my $mhz) = split(/\s+/, $line);
		(my $socket, my $core) = get_cpu_socket_core($cpuid, \@cpu_topology);

		$timestamp_ms = calc_timestamp($time);
		$sar{cpu}{"frequency_MHz"}{sprintf "cpu_%02d", $cpuid}{$timestamp_ms} = $mhz;
		next;
	}

	# 03:25:51 AM      TEMP      degC     %temp DEVICE
	# 03:25:56 AM         1      8.30      0.00 acpitz-virtual-0
	# 03:25:56 AM         2     18.00     22.50 coretemp-isa-0000

	# 03:25:51 AM     BUS  idvendor    idprod  maxpower manufact                product
	# 03:25:56 AM       6       3f0      7029         0 HP                      Virtual Keyboard

	# 03:25:51 AM  MBfsfree  MBfsused   %fsused  %ufsused     Ifree     Iused    %Iused FILESYSTEM
	# 03:25:56 AM     19754     31421     61.40     61.40  52237292    191508      0.37 /dev/mapper/rhel72_perf48-root
	# 03:25:56 AM    419598    140058     25.03     25.03 573360465      6959      0.00 /dev/sdb
	# 03:25:56 AM       269       227     45.79     45.79    511655       345      0.07 /dev/sda1
	# 03:25:56 AM    194709      5394      2.70      2.70 204722751    282049      0.14 /dev/mapper/rhel72_perf48-home
	# 03:25:56 AM     19754     31421     61.40     61.40  52237292    191508      0.37 /dev/mapper/rhel72_perf48-root
}
close(SAR_TXT);
$graph_type{cpu}{"all_cpu_busy"} = "stackedAreaChart";
my $label;
foreach $label (keys %{ $sar{per_cpu}} ) {
	$graph_type{per_cpu}{$label} = "stackedAreaChart";
}
gen_data(\%sar, \%graph_type, \%graph_threshold, $dir);
