capturing current reality

This commit is contained in:
2019-02-24 19:46:07 -06:00
parent aeed0edb38
commit f7c0203827
19 changed files with 907 additions and 29 deletions

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# Detects which OS and if it is Linux then it will detect which Linux Distribution.
OS=`uname -s`
REV=`uname -r`
MACH=`uname -m`
if [ "${OS}" = "SunOS" ] ; then
OS=Solaris
ARCH=`uname -p`
OSSTR="${OS} ${REV}(${ARCH} `uname -v`)"
elif [ "${OS}" = "AIX" ] ; then
OSSTR="${OS} `oslevel` (`oslevel -r`)"
elif [ "${OS}" = "Linux" ] ; then
KERNEL=`uname -r`
if [ -f /etc/fedora-release ]; then
DIST=$(cat /etc/fedora-release | awk '{print $1}')
REV=`cat /etc/fedora-release | sed s/.*release\ // | sed s/\ .*//`
elif [ -f /etc/redhat-release ] ; then
DIST=$(cat /etc/redhat-release | awk '{print $1}')
if [ "${DIST}" = "CentOS" ]; then
DIST="CentOS"
elif [ "${DIST}" = "Mandriva" ]; then
DIST="Mandriva"
PSEUDONAME=`cat /etc/mandriva-release | sed s/.*\(// | sed s/\)//`
REV=`cat /etc/mandriva-release | sed s/.*release\ // | sed s/\ .*//`
elif [ -f /etc/oracle-release ]; then
DIST="Oracle"
else
DIST="RedHat"
fi
PSEUDONAME=`cat /etc/redhat-release | sed s/.*\(// | sed s/\)//`
REV=`cat /etc/redhat-release | sed s/.*release\ // | sed s/\ .*//`
elif [ -f /etc/mandrake-release ] ; then
DIST='Mandrake'
PSEUDONAME=`cat /etc/mandrake-release | sed s/.*\(// | sed s/\)//`
REV=`cat /etc/mandrake-release | sed s/.*release\ // | sed s/\ .*//`
elif [ -f /etc/devuan_version ] ; then
DIST="Devuan `cat /etc/devuan_version`"
REV=""
elif [ -f /etc/debian_version ] ; then
DIST="Debian `cat /etc/debian_version`"
REV=""
ID=`lsb_release -i | awk -F ':' '{print $2}' | sed 's/ //g'`
if [ "${ID}" = "Raspbian" ] ; then
DIST="Raspbian `cat /etc/debian_version`"
fi
elif [ -f /etc/gentoo-release ] ; then
DIST="Gentoo"
REV=$(tr -d '[[:alpha:]]' </etc/gentoo-release | tr -d " ")
elif [ -f /etc/arch-release ] ; then
DIST="Arch Linux"
REV="" # Omit version since Arch Linux uses rolling releases
IGNORE_LSB=1 # /etc/lsb-release would overwrite $REV with "rolling"
elif [ -f /etc/os-release ] ; then
DIST=$(grep '^NAME=' /etc/os-release | cut -d= -f2- | tr -d '"')
REV=$(grep '^VERSION_ID=' /etc/os-release | cut -d= -f2- | tr -d '"')
elif [ -f /etc/openwrt_version ] ; then
DIST="OpenWrt"
REV=$(cat /etc/openwrt_version)
elif [ -f /etc/pld-release ] ; then
DIST=$(cat /etc/pld-release)
REV=""
elif [ -f /etc/SuSE-release ] ; then
DIST=$(echo SLES $(grep VERSION /etc/SuSE-release | cut -d = -f 2 | tr -d " "))
REV=$(echo SP$(grep PATCHLEVEL /etc/SuSE-release | cut -d = -f 2 | tr -d " "))
fi
if [ -f /etc/lsb-release -a "${IGNORE_LSB}" != 1 ] ; then
LSB_DIST=$(lsb_release -si)
LSB_REV=$(lsb_release -sr)
if [ "$LSB_DIST" != "" ] ; then
DIST=$LSB_DIST
fi
if [ "$LSB_REV" != "" ] ; then
REV=$LSB_REV
fi
fi
if [ "`uname -a | awk '{print $(NF)}'`" = "DD-WRT" ] ; then
DIST="dd-wrt"
fi
if [ -n "${REV}" ]
then
OSSTR="${DIST} ${REV}"
else
OSSTR="${DIST}"
fi
elif [ "${OS}" = "Darwin" ] ; then
if [ -f /usr/bin/sw_vers ] ; then
OSSTR=`/usr/bin/sw_vers|grep -v Build|sed 's/^.*:.//'| tr "\n" ' '`
fi
elif [ "${OS}" = "FreeBSD" ] ; then
OSSTR=`/usr/bin/uname -mior`
fi
echo ${OSSTR}

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
################################################################
# copy this script to somewhere like /opt and make chmod +x it #
# edit your snmpd.conf and include #
# extend ntp-client /opt/ntp-client.sh #
# restart snmpd and activate the app for desired host #
# please make sure you have the path/binaries below #
################################################################
# Binaries and paths required #
################################################################
BIN_NTPQ="$(command -v ntpq)"
BIN_GREP="$(command -v grep)"
BIN_TR="$(command -v tr)"
BIN_CUT="$(command -v cut)"
################################################################
# Don't change anything unless you know what are you doing #
################################################################
CMD1=`$BIN_NTPQ -c rv | $BIN_GREP 'jitter' | $BIN_TR '\n' ' '`
IFS=', ' read -r -a array <<< "$CMD1"
for value in 2 3 4 5 6
do
echo ${array["$value"]} | $BIN_CUT -d "=" -f 2
done

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
################################################################
# copy this script to /etc/snmp/ and make it executable: #
# chmod +x /etc/snmp/os-updates.sh #
# ------------------------------------------------------------ #
# edit your snmpd.conf and include: #
# extend osupdate /opt/os-updates.sh #
#--------------------------------------------------------------#
# restart snmpd and activate the app for desired host #
#--------------------------------------------------------------#
# please make sure you have the path/binaries below #
################################################################
BIN_WC='/usr/bin/wc'
BIN_GREP='/bin/grep'
CMD_GREP='-c'
CMD_WC='-l'
BIN_ZYPPER='/usr/bin/zypper'
CMD_ZYPPER='-q lu'
BIN_YUM='/usr/bin/yum'
CMD_YUM='-q check-update'
BIN_DNF='/usr/bin/dnf'
CMD_DNF='-q check-update'
BIN_APT='/usr/bin/apt-get'
CMD_APT='-qq -s upgrade'
BIN_PACMAN='/usr/bin/pacman'
CMD_PACMAN='-Sup'
################################################################
# Don't change anything unless you know what are you doing #
################################################################
if [ -f $BIN_ZYPPER ]; then
# OpenSUSE
UPDATES=`$BIN_ZYPPER $CMD_ZYPPER | $BIN_WC $CMD_WC`
if [ $UPDATES -ge 2 ]; then
echo $(($UPDATES-2));
else
echo "0";
fi
elif [ -f $BIN_DNF ]; then
# Fedora
UPDATES=`$BIN_DNF $CMD_DNF | $BIN_WC $CMD_WC`
if [ $UPDATES -ge 1 ]; then
echo $(($UPDATES-1));
else
echo "0";
fi
elif [ -f $BIN_PACMAN ]; then
# Arch
UPDATES=`$BIN_PACMAN $CMD_PACMAN | $BIN_WC $CMD_WC`
if [ $UPDATES -ge 1 ]; then
echo $(($UPDATES-1));
else
echo "0";
fi
elif [ -f $BIN_YUM ]; then
# CentOS / Redhat
UPDATES=`$BIN_YUM $CMD_YUM | $BIN_WC $CMD_WC`
if [ $UPDATES -ge 1 ]; then
echo $(($UPDATES-1));
else
echo "0";
fi
elif [ -f $BIN_APT ]; then
# Debian / Devuan / Ubuntu
UPDATES=`$BIN_APT $CMD_APT | $BIN_GREP $CMD_GREP 'Inst'`
if [ $UPDATES -ge 1 ]; then
echo $UPDATES;
else
echo "0";
fi
else
echo "0";
fi

View File

@@ -0,0 +1,13 @@
#!/bin/bash
#Written by Valec 2006. Steal and share.
#Get postfix queue lengths
#extend mailq /opt/observer/scripts/getmailq.sh
QUEUES="incoming active deferred hold"
for i in $QUEUES; do
COUNT=`qshape $i | grep TOTAL | awk '{print $2}'`
printf "$COUNT\n"
done

View File

@@ -0,0 +1,363 @@
#!/usr/bin/env perl
#Copyright (c) 2017, Zane C. Bowers-Hadley
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without modification,
#are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
#IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
#BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
#THE POSSIBILITY OF SUCH DAMAGE.
=for comment
Add this to snmpd.conf like below.
extend smart /etc/snmp/smart
Then add to root's cron tab, if you have more than a few disks.
*/3 * * * * /etc/snmp/smart -u
You will also need to create the config file, which defaults to the same path as the script,
but with .config appended. So if the script is located at /etc/snmp/smart, the config file
will be /etc/snmp/smart.config. Alternatively you can also specific a config via -c.
Anything starting with a # is comment. The format for variables is $variable=$value. Empty
lines are ignored. Spaces and tabes at either the start or end of a line are ignored. Any
line with out a = or # are treated as a disk.
#This is a comment
cache=/var/cache/smart
smartctl=/usr/local/sbin/smartctl
useSN=0
ada0
ada1
The variables are as below.
cache = The path to the cache file to use. Default: /var/cache/smart
smartctl = The path to use for smartctl. Default: /usr/bin/env smartctl
useSN = If set to 1, it will use the disks SN for reporting instead of the device name.
1 is the default. 0 will use the device name.
If you want to guess at the configuration, call it with -g and it will print out what it thinks
it should be.
=cut
##
## You should not need to touch anything below here.
##
use warnings;
use strict;
use Getopt::Std;
my $cache='/var/cache/smart';
my $smartctl='/usr/bin/env smartctl';
my @disks;
my $useSN=1;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
sub main::VERSION_MESSAGE {
print "SMART SNMP extend 0.0.0\n";
};
sub main::HELP_MESSAGE {
print "\n".
"-u Update '".$cache."'\n".
"-g Guess at the config and print it to STDOUT.\n".
"-c <config> The config file to use.\n";
}
#gets the options
my %opts=();
getopts('ugc:', \%opts);
# guess if asked
if ( defined( $opts{g} ) ){
#get what path to use for smartctl
$smartctl=`which smartctl`;
chomp($smartctl);
if ( $? != 0 ){
warn("'which smartctl' failed with a exit code of $?");
exit 1;
}
#try to touch the default cache location and warn if it can't be done
system('touch '.$cache.'>/dev/null');
if ( $? != 0 ){
$cache='#Could not touch '.$cache. "You will need to manually set it\n".
"cache=?\n";
}else{
$cache='cache='.$cache."\n";
}
my %found_disks;
#check for drives named /dev/sd*
my @matches=glob('/dev/sd*');
@matches=grep(!/[0-9]/, @matches);
my $matches_int=0;
while ( defined( $matches[$matches_int] ) ){
my $device=$matches[$matches_int];
system( $smartctl.' -A '.$device.' > /dev/null' );
if ( $? == 0 ){
$device =~ s/\/dev\///;
$found_disks{$device}=1;
}
$matches_int++;
}
#check for drives named /dev/ada*
@matches=glob('/dev/ada*');
@matches=grep(!/[ps]/, @matches);
$matches_int=0;
while ( defined( $matches[$matches_int] ) ){
my $device=$matches[$matches_int];
system( $smartctl.' -A '.$device.' > /dev/null' );
if ( $? == 0 ){
$device =~ s/\/dev\///;
$found_disks{$device}=1;
}
$matches_int++;
}
#check for drives named /dev/da*
@matches=glob('/dev/da*');
@matches=grep(!/[ps]/, @matches);
$matches_int=0;
while ( defined( $matches[$matches_int] ) ){
my $device=$matches[$matches_int];
system( $smartctl.' -A '.$device.' > /dev/null' );
if ( $? == 0 ){
$device =~ s/\/dev\///;
$found_disks{$device}=1;
}
$matches_int++;
}
#have smartctl scan and see if it finds anythings not get found
my $scan_output=`$smartctl --scan-open`;
my @scan_outputA=split(/\n/, $scan_output);
@scan_outputA=grep(!/ses[0-9]/, @scan_outputA); # not a disk, but may or may not have SMART attributes
@scan_outputA=grep(!/pass[0-9]/, @scan_outputA); # very likely a duplicate and a disk under another name
$matches_int=0;
while ( defined( $scan_outputA[$matches_int] ) ){
my $device=$scan_outputA[$matches_int];
$device =~ s/ .*//;
system( $smartctl.' -A '.$device.' > /dev/null' );
if ( $? == 0 ){
$device =~ s/\/dev\///;
$found_disks{$device}=1;
}
$matches_int++;
}
print "useSN=0\n".'smartctl='.$smartctl."\n".
$cache.
join( "\n", keys(%found_disks) )."\n";
exit 0;
}
#get which config file to use
my $config=$0.'.config';
if ( defined( $opts{c} ) ){
$config=$opts{c};
}
#reads the config file, optionally
my $config_file='';
open(my $readfh, "<", $config) or die "Can't open '".$config."'";
read($readfh , $config_file , 1000000);
close($readfh);
#parse the config file and remove comments and empty lines
my @configA=split(/\n/, $config_file);
@configA=grep(!/^$/, @configA);
@configA=grep(!/^\#/, @configA);
@configA=grep(!/^[\s\t]*$/, @configA);
my $configA_int=0;
while ( defined( $configA[$configA_int] ) ){
my $line=$configA[$configA_int];
$line=~s/^[\t\s]+//;
$line=~s/[\t\s]+$//;
my ( $var, $val )=split(/=/, $line, 2);
if ( $var eq 'cache' ){
$cache=$val;
}
if ( $var eq 'smartctl' ){
$smartctl=$val;
}
if ( $var eq 'useSN' ){
$useSN=$val;
}
if ( !defined( $val ) ){
push(@disks, $var);
}
$configA_int++;
}
#if set to 1, no cache will be written and it will be printed instead
my $noWrite=0;
# if no -u, it means we are being called from snmped
if ( ! defined( $opts{u} ) ){
# if the cache file exists, print it, otherwise assume one is not being used
if ( -f $cache ){
my $old='';
open(my $readfh, "<", $cache) or die "Can't open '".$cache."'";
read($readfh , $old , 1000000);
close($readfh);
print $old;
exit 0;
}else{
$opts{u}=1;
$noWrite=1;
}
}
my $toReturn='';
my $int=0;
while ( defined($disks[$int]) ) {
my $disk=$disks[$int];
my $disk_sn=$disk;
my $output=`$smartctl -A /dev/$disk`;
my %IDs=( '5'=>'null',
'10'=>'null',
'173'=>'null',
'177'=>'null',
'183'=>'null',
'184'=>'null',
'187'=>'null',
'188'=>'null',
'190'=>'null',
'194'=>'null',
'196'=>'null',
'197'=>'null',
'198'=>'null',
'199'=>'null',
'231'=>'null',
'233'=>'null',
);
my @outputA=split( /\n/, $output );
my $outputAint=0;
while ( defined($outputA[$outputAint]) ) {
my $line=$outputA[$outputAint];
$line=~s/^ +//;
$line=~s/ +/ /g;
if ( $line =~ /^[0123456789]+ / ) {
my @lineA=split(/\ /, $line, 10);
my $raw=$lineA[9];
my $id=$lineA[0];
# single int raw values
if (
( $id == 5 ) ||
( $id == 10 ) ||
( $id == 173 ) ||
( $id == 177 ) ||
( $id == 183 ) ||
( $id == 184 ) ||
( $id == 187 ) ||
( $id == 196 ) ||
( $id == 197 ) ||
( $id == 198 ) ||
( $id == 199 ) ||
( $id == 231 ) ||
( $id == 233 )
) {
$IDs{$id}=$raw;
}
# 188, Command_Timeout
if ( $id == 188 ) {
my $total=0;
my @rawA=split( /\ /, $raw );
my $rawAint=0;
while ( defined( $rawA[$rawAint] ) ) {
$total=$total+$rawA[$rawAint];
$rawAint++;
}
$IDs{$id}=$total;
}
# 190, airflow temp
# 194, temp
if (
( $id == 190 ) ||
( $id == 194 )
) {
my ( $temp )=split(/\ /, $raw);
$IDs{$id}=$temp;
}
}
$outputAint++;
}
#get the selftest logs
$output=`$smartctl -l selftest /dev/$disk`;
@outputA=split( /\n/, $output );
my $completed=scalar grep(/Completed without error/, @outputA);
my $interrupted=scalar grep(/Interrupted/, @outputA);
my $read_failure=scalar grep(/read failure/, @outputA);
my $unknown_failure=scalar grep(/unknown failure/, @outputA);
my $extended=scalar grep(/Extended/, @outputA);
my $short=scalar grep(/Short/, @outputA);
my $conveyance=scalar grep(/Conveyance/, @outputA);
my $selective=scalar grep(/Selective/, @outputA);
# get the drive serial number, if needed
my $disk_id=$disk;
if ( $useSN ){
while (`$smartctl -i /dev/$disk` =~ /Serial Number:(.*)/g) {
$disk_id = $1;
$disk_id =~ s/^\s+|\s+$//g;
}
}
$toReturn=$toReturn.$disk_id.','.$IDs{'5'}.','.$IDs{'10'}.','.$IDs{'173'}.','.$IDs{'177'}.','.$IDs{'183'}.','.$IDs{'184'}.','.$IDs{'187'}.','.$IDs{'188'}
.','.$IDs{'190'} .','.$IDs{'194'}.','.$IDs{'196'}.','.$IDs{'197'}.','.$IDs{'198'}.','.$IDs{'199'}.','.$IDs{'231'}.','.$IDs{'233'}.','.
$completed.','.$interrupted.','.$read_failure.','.$unknown_failure.','.$extended.','.$short.','.$conveyance.','.$selective."\n";
$int++;
}
if ( ! $noWrite ){
open(my $writefh, ">", $cache) or die "Can't open '".$cache."'";
print $writefh $toReturn;
close($writefh);
}else{
print $toReturn;
}

View File

@@ -0,0 +1,3 @@
smartctl=/usr/sbin/smartctl
cache=/var/cache/smart
sda

View File

@@ -0,0 +1,266 @@
#!/usr/bin/env perl
=head1 DESCRIPTION
This is a SNMP extend for ZFS and FreeBSD for use with LibreNMS.
For more information, see L<https://docs.librenms.org/#Extensions/Applications/#zfs>.
=head1 SWITCHES
=head2 -p
Pretty print the JSON.
=head1 SNMPD SETUP EXAMPLES
extend zfs /etc/snmp/zfs-freebsd
=cut
#Copyright (c) 2017, Zane C. Bowers-Hadley
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without modification,
#are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
#IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
#BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
#THE POSSIBILITY OF SUCH DAMAGE.
# Many thanks to Ben Rockwood, Jason J. Hellenthal, and Martin Matuska
# for zfs-stats and figuring out the math for all the stats
use strict;
use warnings;
use JSON;
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
sub main::VERSION_MESSAGE {
print "FreeBSD ZFS stats extend 0.0.0\n";
}
sub main::HELP_MESSAGE {
}
#this will be dumped to json at the end
my %tojson;
#gets the options
my %opts=();
getopts('p', \%opts);
my $sysctls;
my @to_pull=(
'kstat.zfs',
'vfs.zfs',
);
my @sysctls_pull = `/sbin/sysctl -q @to_pull`;
foreach my $stat (@sysctls_pull) {
chomp( $stat );
my ( $var, $val ) = split(/:/, $stat, 2);
$val =~ s/^ //;
$sysctls->{$var}=$val;
}
# does not seem to exist for me, but some of these don't seem to be created till needed
if ( ! defined( $sysctls->{"kstat.zfs.misc.arcstats.recycle_miss"} ) ) {
$sysctls->{"kstat.zfs.misc.arcstats.recycle_miss"}=0;
}
##
## ARC misc
##
$tojson{deleted}=$sysctls->{"kstat.zfs.misc.arcstats.deleted"};
$tojson{evict_skip}=$sysctls->{"kstat.zfs.misc.arcstats.evict_skip"};
$tojson{mutex_skip}=$sysctls->{'kstat.zfs.misc.arcstats.mutex_miss'};
$tojson{recycle_miss}=$sysctls->{"kstat.zfs.misc.arcstats.recycle_miss"};
##
## ARC size
##
my $target_size_percent = $sysctls->{"kstat.zfs.misc.arcstats.c"} / $sysctls->{"kstat.zfs.misc.arcstats.c_max"} * 100;
my $arc_size_percent = $sysctls->{"kstat.zfs.misc.arcstats.size"} / $sysctls->{"kstat.zfs.misc.arcstats.c_max"} * 100;
my $target_size_adaptive_ratio = $sysctls->{"kstat.zfs.misc.arcstats.c"} / $sysctls->{"kstat.zfs.misc.arcstats.c_max"};
my $min_size_percent = $sysctls->{"kstat.zfs.misc.arcstats.c_min"} / $sysctls->{"kstat.zfs.misc.arcstats.c_max"} * 100;
$tojson{arc_size}=$sysctls->{"kstat.zfs.misc.arcstats.size"};
$tojson{target_size_max}=$sysctls->{"kstat.zfs.misc.arcstats.c_max"};
$tojson{target_size_min}=$sysctls->{"kstat.zfs.misc.arcstats.c_min"};
$tojson{target_size}=$sysctls->{"kstat.zfs.misc.arcstats.c"};
$tojson{target_size_per}=$target_size_percent;
$tojson{arc_size_per}=$arc_size_percent;
$tojson{target_size_arat}=$target_size_adaptive_ratio;
$tojson{min_size_per}=$min_size_percent;
##
## ARC size breakdown
##
my $mfu_size;
my $recently_used_percent;
my $frequently_used_percent;
if ( $sysctls->{"kstat.zfs.misc.arcstats.size"} >= $sysctls->{"kstat.zfs.misc.arcstats.c"} ){
$mfu_size = $sysctls->{"kstat.zfs.misc.arcstats.size"} - $sysctls->{"kstat.zfs.misc.arcstats.p"};
$recently_used_percent = $sysctls->{"kstat.zfs.misc.arcstats.p"} / $sysctls->{"kstat.zfs.misc.arcstats.size"} * 100;
$frequently_used_percent = $mfu_size / $sysctls->{"kstat.zfs.misc.arcstats.size"} * 100;
}else{
$mfu_size = $sysctls->{"kstat.zfs.misc.arcstats.c"} - $sysctls->{"kstat.zfs.misc.arcstats.p"};
$recently_used_percent = $sysctls->{"kstat.zfs.misc.arcstats.p"} / $sysctls->{"kstat.zfs.misc.arcstats.c"} * 100;
$frequently_used_percent = $mfu_size / $sysctls->{"kstat.zfs.misc.arcstats.c"} * 100;
}
$tojson{mfu_size}=$mfu_size;
$tojson{p}=$sysctls->{"kstat.zfs.misc.arcstats.p"};
$tojson{rec_used_per}=$recently_used_percent;
$tojson{freq_used_per}=$frequently_used_percent;
##
## ARC efficiency
##
my $arc_hits = $sysctls->{"kstat.zfs.misc.arcstats.hits"};
my $arc_misses = $sysctls->{"kstat.zfs.misc.arcstats.misses"};
my $demand_data_hits = $sysctls->{"kstat.zfs.misc.arcstats.demand_data_hits"};
my $demand_data_misses = $sysctls->{"kstat.zfs.misc.arcstats.demand_data_misses"};
my $demand_metadata_hits = $sysctls->{"kstat.zfs.misc.arcstats.demand_metadata_hits"};
my $demand_metadata_misses = $sysctls->{"kstat.zfs.misc.arcstats.demand_metadata_misses"};
my $mfu_ghost_hits = $sysctls->{"kstat.zfs.misc.arcstats.mfu_ghost_hits"};
my $mfu_hits = $sysctls->{"kstat.zfs.misc.arcstats.mfu_hits"};
my $mru_ghost_hits = $sysctls->{"kstat.zfs.misc.arcstats.mru_ghost_hits"};
my $mru_hits = $sysctls->{"kstat.zfs.misc.arcstats.mru_hits"};
my $prefetch_data_hits = $sysctls->{"kstat.zfs.misc.arcstats.prefetch_data_hits"};
my $prefetch_data_misses = $sysctls->{"kstat.zfs.misc.arcstats.prefetch_data_misses"};
my $prefetch_metadata_hits = $sysctls->{"kstat.zfs.misc.arcstats.prefetch_metadata_hits"};
my $prefetch_metadata_misses = $sysctls->{"kstat.zfs.misc.arcstats.prefetch_metadata_misses"};
my $anon_hits = $arc_hits - ($mfu_hits + $mru_hits + $mfu_ghost_hits + $mru_ghost_hits);
my $arc_accesses_total = $arc_hits + $arc_misses;
my $demand_data_total = $demand_data_hits + $demand_data_misses;
my $prefetch_data_total = $prefetch_data_hits + $prefetch_data_misses;
my $real_hits = $mfu_hits + $mru_hits;
my $cache_hit_percent = $arc_hits / $arc_accesses_total * 100;
my $cache_miss_percent = $arc_misses / $arc_accesses_total * 100;
my $actual_hit_percent = $real_hits / $arc_accesses_total * 100;
my $data_demand_percent = $demand_data_hits / $demand_data_total * 100;
my $data_prefetch_percent;
if ( $prefetch_data_total != 0 ) {
$data_prefetch_percent = $prefetch_data_hits / $prefetch_data_total * 100;
}else{
$data_prefetch_percent = 0;
}
my $anon_hits_percent;
if ( $anon_hits != 0 ) {
$anon_hits_percent = $anon_hits / $arc_hits * 100;
}else{
$anon_hits_percent=0;
}
my $mru_percent = $mru_hits / $arc_hits * 100;
my $mfu_percent = $mfu_hits / $arc_hits * 100;
my $mru_ghost_percent = $mru_ghost_hits / $arc_hits * 100;
my $mfu_ghost_percent = $mfu_ghost_hits / $arc_hits * 100;
my $demand_hits_percent = $demand_data_hits / $arc_hits * 100;
my $prefetch_hits_percent = $prefetch_data_hits / $arc_hits * 100;
my $metadata_hits_percent = $demand_metadata_hits / $arc_hits * 100;
my $prefetch_metadata_hits_percent = $prefetch_metadata_hits / $arc_hits * 100;
my $demand_misses_percent = $demand_data_misses / $arc_misses * 100;
my $prefetch_misses_percent = $prefetch_data_misses / $arc_misses * 100;
my $metadata_misses_percent = $demand_metadata_misses / $arc_misses * 100;
my $prefetch_metadata_misses_percent = $prefetch_metadata_misses / $arc_misses * 100;
# ARC misc. efficient stats
$tojson{arc_hits}=$arc_hits;
$tojson{arc_misses}=$arc_misses;
$tojson{demand_data_hits}=$demand_data_hits;
$tojson{demand_data_misses}=$demand_data_misses;
$tojson{demand_meta_hits}=$demand_metadata_hits;
$tojson{demand_meta_misses}=$demand_metadata_misses;
$tojson{mfu_ghost_hits}=$mfu_ghost_hits;
$tojson{mfu_hits}=$mfu_hits;
$tojson{mru_ghost_hits}=$mru_ghost_hits;
$tojson{mru_hits}=$mru_hits;
$tojson{pre_data_hits}=$prefetch_data_hits;
$tojson{pre_data_misses}=$prefetch_data_misses;
$tojson{pre_meta_hits}=$prefetch_metadata_hits;
$tojson{pre_meta_misses}=$prefetch_metadata_misses;
$tojson{anon_hits}=$anon_hits;
$tojson{arc_accesses_total}=$arc_accesses_total;
$tojson{demand_data_total}=$demand_data_total;
$tojson{pre_data_total}=$prefetch_data_total;
$tojson{real_hits}=$real_hits;
# ARC efficient percents
$tojson{cache_hits_per}=$cache_hit_percent;
$tojson{cache_miss_per}=$cache_miss_percent;
$tojson{actual_hit_per}=$actual_hit_percent;
$tojson{data_demand_per}=$data_demand_percent;
$tojson{data_pre_per}=$data_prefetch_percent;
$tojson{anon_hits_per}=$anon_hits_percent;
$tojson{mru_per}=$mru_percent;
$tojson{mfu_per}=$mfu_percent;
$tojson{mru_ghost_per}=$mru_ghost_percent;
$tojson{mfu_ghost_per}=$mfu_ghost_percent;
$tojson{demand_hits_per}=$demand_hits_percent;
$tojson{pre_hits_per}=$prefetch_hits_percent;
$tojson{meta_hits_per}=$metadata_hits_percent;
$tojson{pre_meta_hits_per}=$prefetch_metadata_hits_percent;
$tojson{demand_misses_per}=$demand_misses_percent;
$tojson{pre_misses_per}=$prefetch_misses_percent;
$tojson{meta_misses_per}=$metadata_misses_percent;
$tojson{pre_meta_misses_per}=$prefetch_metadata_misses_percent;
#process each pool and shove them into JSON
my $zpool_output=`/sbin/zpool list -pH`;
my @pools=split( /\n/, $zpool_output );
my $pools_int=0;
my @toShoveIntoJSON;
while ( defined( $pools[$pools_int] ) ) {
my %newPool;
my $pool=$pools[$pools_int];
$pool =~ s/\t/,/g;
$pool =~ s/\,\-\,/\,0\,/g;
$pool =~ s/\%//g;
$pool =~ s/\,([0-1\.]*)x\,/,$1,/;
( $newPool{name}, $newPool{size}, $newPool{alloc}, $newPool{free}, $newPool{expandsz}, $newPool{frag}, $newPool{cap}, $newPool{dedup} )=split(/\,/, $pool);
push(@toShoveIntoJSON, \%newPool);
$pools_int++;
}
$tojson{pools}=\@toShoveIntoJSON;
my $j=JSON->new;
if ( $opts{p} ){
$j->pretty(1);
}
print $j->encode( \%tojson );
if (! $opts{p} ){
print "\n";
}
exit 0;