parent
2c7a08aa8a
commit
0ebc7a7180
Binary file not shown.
@ -0,0 +1,5 @@
|
||||
# TA-metricator-hec-for-nmon
|
||||
|
||||
Copyright 2017 Octamis - Copyright 2017 Guilhem Marchand
|
||||
|
||||
All rights reserved.
|
||||
@ -0,0 +1,258 @@
|
||||
# nmon.conf.spec
|
||||
|
||||
# This file contains possibles attributes and values you can use to configure nmon processes generation.
|
||||
|
||||
# There is an nmon.conf in $SPLUNK_HOME/etc/[nmon|TA-nmon|PA-nmon]/default/. To set custom configurations,
|
||||
# place an nmon.conf in $SPLUNK_HOME/etc/[nmon|TA-nmon|PA-nmon]/default/.
|
||||
|
||||
# *** FILE ENCODING: UTF-8 ! ***
|
||||
# When creating a local/nmon.conf, pay attention to file encoding specially when working under Windows.
|
||||
# The file must be UTF-8 encoded or you may run in trouble.
|
||||
|
||||
# *** DON'T MODIFY THIS FILE ***
|
||||
|
||||
########################################################################################################################
|
||||
### NMON COLLECT OPTIONS ###
|
||||
########################################################################################################################
|
||||
|
||||
# The metricator_helper.sh input script is set by default to run every 60 seconds
|
||||
# If Nmon is not running, the script will start Nmon using the configuration above
|
||||
|
||||
###
|
||||
### FIFO options:
|
||||
###
|
||||
|
||||
# Using FIFO files (named pipe) are now used to minimize the CPU footprint of the technical addons
|
||||
# As such, it is not required anymore to use short cycle of Nmon run to reduce the CPU usage
|
||||
|
||||
# You can still want to manage the volume of data to be generated by managing the interval and snapshot values
|
||||
# as a best practice recommendation, the time to live of nmon processes writing to FIFO should be 24 hours
|
||||
|
||||
# value for interval: time in seconds between 2 performance measures
|
||||
fifo_interval=<value>
|
||||
|
||||
# value for snapshot: number of measure to perform
|
||||
fifo_snapshot=<value>
|
||||
|
||||
########################################################################################################################
|
||||
### VARIOUS COMMON OPTIONS ###
|
||||
########################################################################################################################
|
||||
|
||||
# Time in seconds of margin before running a new iteration of Nmon process to prevent data gaps between 2 iterations of Nmon
|
||||
# the metricator_helper.sh script will spawn a new Nmon process when the age in seconds of the current process gets higher than this value
|
||||
|
||||
# The endtime is evaluated the following way:
|
||||
# endtime=$(( ${interval} * ${snapshot} - ${endtime_margin} ))
|
||||
|
||||
# When the endtime gets higher than the endtime_margin, a new Nmon process will be spawned
|
||||
# default value to 240 seconds which will start a new process 4 minutes before the current process ends
|
||||
|
||||
# Setting this value to "0" will totally disable this feature
|
||||
|
||||
# Default value:
|
||||
# endtime_margin="240"
|
||||
|
||||
endtime_margin=<value>
|
||||
|
||||
### NFS OPTIONS ###
|
||||
|
||||
# Change to "1" to activate NFS V2 / V3 (option -N) for AIX hosts
|
||||
# Default value:
|
||||
# AIX_NFS23="0"
|
||||
|
||||
AIX_NFS23=<string>
|
||||
|
||||
# Change to "1" to activate NFS V4 (option -NN) for AIX hosts
|
||||
# Default value:
|
||||
# AIX_NFS4="0"
|
||||
|
||||
AIX_NFS4=<string>
|
||||
|
||||
# Change to "1" to activate NFS V2 / V3 / V4 (option -N) for Linux hosts
|
||||
# Note: Some versions of Nmon introduced a bug that makes Nmon to core when activating NFS, ensure your version is not outdated
|
||||
# Default value:
|
||||
# Linux_NFS="0"
|
||||
|
||||
Linux_NFS=<string>
|
||||
|
||||
########################################################################################################################
|
||||
### LINUX OPTIONS ###
|
||||
########################################################################################################################
|
||||
|
||||
# Change the priority applied while looking at nmon binary
|
||||
# by default, the metricator_helper.sh script will use any nmon binary found in PATH
|
||||
# Set to "1" to give the priority to embedded nmon binaries
|
||||
# Note: Since release 1.6.07, priority is given by default to embedded binaries
|
||||
|
||||
# Default value:
|
||||
# Linux_embedded_nmon_priority="1"
|
||||
|
||||
Linux_embedded_nmon_priority=<string>
|
||||
|
||||
# Change the limit for processes and disks capture of nmon for Linux
|
||||
# In default configuration, nmon will capture most of the process table by capturing main consuming processes
|
||||
# This function is percentage limit of CPU time, with a default limit of 0.01
|
||||
# Changing this value can influence the volume of data to be generated, and the associated CPU overhead for that data to be parsed
|
||||
|
||||
# Possible values are:
|
||||
# Linux_unlimited_capture="0" --> Default nmon behavior, capture main processes (no -I option)
|
||||
# Linux_unlimited_capture="-1" --> Set the capture mode to unlimited (-I -1)
|
||||
# Linux_unlimited_capture="x.xx" --> Set the percentage limit to a custom value, ex: "0.01" will set "-I 0.01"
|
||||
Linux_unlimited_capture=<value>
|
||||
|
||||
# Set the maximum number of devices collected by Nmon, default is set to 1500 devices
|
||||
# Increase this value if you have systems with more devices
|
||||
# Up to 3000 devices will be taken in charge by the Application (hard limit in nmonparser.py / nmonparser.pl)
|
||||
|
||||
# Default value:
|
||||
# Linux_devices="1500"
|
||||
|
||||
Linux_devices=<value>
|
||||
|
||||
# Enable disks extended statistics (DG*)
|
||||
# Default is true, which activates and generates DG statistics
|
||||
Linux_disk_dg_enable=<string>
|
||||
|
||||
# Name of the User Defined Disk Groups file, "auto" generates this for you
|
||||
Linux_disk_dg_group=<value>
|
||||
|
||||
########################################################################################################################
|
||||
### SOLARIS OPTIONS ###
|
||||
########################################################################################################################
|
||||
|
||||
# Change to "1" to activate VxVM volumes IO statistics
|
||||
# Default value:
|
||||
|
||||
# Solaris_VxVM="0"
|
||||
|
||||
Solaris_VxVM=<string>
|
||||
|
||||
# UARG collection (new in Version 1.11), Change to "0" to deactivate, "1" to activate (default is activate)
|
||||
# Default value:
|
||||
|
||||
# Solaris_UARG="1"
|
||||
|
||||
Solaris_UARG=<string>
|
||||
|
||||
########################################################################################################################
|
||||
### AIX OPTIONS ###
|
||||
########################################################################################################################
|
||||
|
||||
# CAUTION: Since release 1.3.0, we use fifo files, which requires the option "-yoverwrite=1"
|
||||
|
||||
# Change this line if you add or remove common options for AIX, do not change NFS options here (see NFS options)
|
||||
# the -p option is mandatory as it is used at launch time to save instance pid
|
||||
|
||||
# Default value:
|
||||
# AIX_options="-f -T -A -d -K -L -M -P -^ -p -yoverwrite=1"
|
||||
|
||||
AIX_options=<string>
|
||||
|
||||
#############################
|
||||
# Application related options
|
||||
#############################
|
||||
|
||||
|
||||
######################
|
||||
# hostname definition:
|
||||
######################
|
||||
|
||||
# This option can be used to force the technical add-on to use the Splunk configured value of the server hostname
|
||||
# If for some reason, you need to use the Splunk host value instead of the system real hostname value, set this value to "1"
|
||||
|
||||
# We will search for the value of host=<value> in $SPLUNK_HOME/etc/system/local/inputs.conf
|
||||
# If no value can be found, or if the file does not exist, we will fallback to the normal behavior
|
||||
|
||||
# Default is use system hostname
|
||||
|
||||
# FQDN management in nmonparser: The --fqdn option is not compatible with the host name override, if the override_sys_hostname
|
||||
# is activated, the --fqdn argument will have no effect
|
||||
|
||||
override_sys_hostname=<string>
|
||||
|
||||
#####################
|
||||
# frameID definition:
|
||||
#####################
|
||||
|
||||
# The frameID definition is an enrichment mechanism used within the application to associate a given host with a given frame identifier
|
||||
# By default, the mapping is operated against the value of "serialnum" which is defined at the raw level by nmon binaries
|
||||
|
||||
# On AIX systems, the serialnum value is equal to the serial number of the frame hosting the partition
|
||||
# On Linux and Solaris systems, the serialnum is equal to the value of the hostname
|
||||
|
||||
# Using this option allows you to override the serialnum value by a static value defined in the nmon.conf configuration file
|
||||
# nmon.conf precedence allows defining the serialnum value on per deployment basis (local/nmon.conf) or on a per server basis (/etc/nmon.conf)
|
||||
|
||||
# default is:
|
||||
# override_sys_serialnum="0"
|
||||
# which lets nmon set the serialnum value
|
||||
|
||||
# Set this value to:
|
||||
# override_sys_serialnum="1"
|
||||
# to activate the serialnum override based on the value defined in:
|
||||
|
||||
# override_sys_serialnum_value="<sting>"
|
||||
# Acceptable values for <string> are letters (lower and upper case), numbers and "-" / "_"
|
||||
|
||||
override_sys_serialnum=<string>
|
||||
override_sys_serialnum_value=<string>
|
||||
|
||||
########################
|
||||
# nmon external metrics:
|
||||
########################
|
||||
|
||||
# nmon external generation management
|
||||
|
||||
# This option will manage the activation or deactivation of the nmon external data generation at the lower level, before it comes to parsers
|
||||
# default is activated (value=1), set to "0" to deactivate
|
||||
|
||||
nmon_external_generation=<string>
|
||||
|
||||
###############
|
||||
# fifo options:
|
||||
###############
|
||||
|
||||
# Fifo options
|
||||
|
||||
# The realtime mode which corresponds to the old mechanism is now deprecated
|
||||
# fifo mode is mandatory
|
||||
|
||||
# Default is "1" which means write to fifo
|
||||
|
||||
mode_fifo=<string>
|
||||
|
||||
#######################
|
||||
# nmon parsers options:
|
||||
#######################
|
||||
|
||||
# consult the documentation to get the full list of available options
|
||||
|
||||
# --mode fifo|colddata --> explicitly manage data in fifo/colddata
|
||||
# --use_fqdn --> use the host fully qualified domain name (default)
|
||||
# --silent --> minimize the processing output to save data volume (deactivated by default)
|
||||
# --show_zero_values --> allows generating metrics with 0 values (default removes any metric with a zero value before it reaches the ingestion)
|
||||
# --no_local_log --> do no write metrics, events and config locally on file-system (activated by default)
|
||||
# --splunk_http_url --> Splunk HEC endpoint URL (must contain the protocol, IP or FQDN and endpoint path)
|
||||
# --splunk_http_token --> Splunk HEC token value
|
||||
# --splunk_metrics_index --> Name of the metrics index (default: os-unix-nmon-metrics)
|
||||
# --splunk_events_index --> Name of the events index (default: os-unix-nmon-events)
|
||||
# --splunk_config_index --> Name of the config index (default: os-unix-nmon-config)
|
||||
|
||||
# In fifo mode, options are sent by the metricator_consumer.sh
|
||||
# In file mode, options are sent by Splunk via the nmon_processing stanza in props.conf
|
||||
|
||||
#
|
||||
# Splunk HEC configuration (http input)
|
||||
#
|
||||
|
||||
# Change the Splunk URL to match your protocol (http vs https) and your access URL
|
||||
# By default, as long the token value is not changed from the demonstration value above, the parser will just do nothing else than writing to local logs
|
||||
|
||||
# For more information, see: http://dev.splunk.com/view/event-collector/SP-CAAAE6M
|
||||
|
||||
# TO CONFIGURE:
|
||||
|
||||
# - copy the default/nmon.conf to local/
|
||||
# - manage your settings in your local nmon.conf
|
||||
|
||||
nmonparser_options=<string>
|
||||
@ -0,0 +1 @@
|
||||
This is where you put any scripts you want to add to this app.
|
||||
@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Program name: create_agent.py
|
||||
# Compatibility: Python 2x
|
||||
# Purpose - Create a customized version of the TA-metricator-hec-for-nmon
|
||||
# Licence:
|
||||
|
||||
# Copyright 2018 Guilhem Marchand
|
||||
|
||||
import sys
|
||||
import os
|
||||
import tarfile
|
||||
import glob
|
||||
import fnmatch
|
||||
import argparse
|
||||
import shutil
|
||||
|
||||
version = '2.0.0'
|
||||
|
||||
####################################################################
|
||||
############# Arguments Parser
|
||||
####################################################################
|
||||
|
||||
# Define Arguments
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-f', action='store', dest='INFILE',
|
||||
help='Name of the tgz archive file')
|
||||
|
||||
parser.add_argument('--agentname', action='store', dest='TARGET',
|
||||
help='Define the TA Agent name and root directory')
|
||||
|
||||
parser.add_argument('--version', action='version', version='%(prog)s ' + version)
|
||||
|
||||
parser.add_argument('--debug', dest='debug', action='store_true')
|
||||
|
||||
parser.set_defaults(debug=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Set debug
|
||||
if args.debug:
|
||||
debug = True
|
||||
|
||||
####################################################################
|
||||
############# Functions
|
||||
####################################################################
|
||||
|
||||
# String replacement function
|
||||
# Can be called by:
|
||||
# findreplace(path, string_to_search, replace_by, file_extension)
|
||||
|
||||
def findreplace(directory, find, replace, filepattern):
|
||||
for path, dirs, files in os.walk(os.path.abspath(directory)):
|
||||
for filename in fnmatch.filter(files, filepattern):
|
||||
filepath = os.path.join(path, filename)
|
||||
|
||||
# Prevents binaries modification
|
||||
if "bin/linux" in filepath:
|
||||
if debug:
|
||||
print("file " + str(filename) + " is binary or binary related")
|
||||
elif "bin/sarmon" in filepath:
|
||||
if debug:
|
||||
print("file " + str(filename) + " is binary or binary related")
|
||||
else:
|
||||
with open(filepath) as f:
|
||||
s = f.read()
|
||||
s = s.replace(find, replace)
|
||||
with open(filepath, "w") as f:
|
||||
f.write(s)
|
||||
|
||||
|
||||
####################################################################
|
||||
############# Main Program
|
||||
####################################################################
|
||||
|
||||
# Check Arguments
|
||||
if len(sys.argv) < 2:
|
||||
print "\n%s" % os.path.basename(sys.argv[0])
|
||||
print "\nThis utility had been designed to allow creating customized agents for the TA-metricator-hec-for-nmon" \
|
||||
" please follow these instructions:\n"
|
||||
print "- Download the current release of the technical add-on"
|
||||
print "- Ensure to have this Python script and the TGZ archive in the same directory"
|
||||
print "- Run the tool: ./create_agent.py and check for available options"
|
||||
print "- After the execution, a new agent package will have been created in the resources directory"
|
||||
print "- Extract its content to your Splunk deployment server, configure the server class, associated clients and" \
|
||||
" deploy the agent"
|
||||
print "- Don't forget to set the application to restart splunkd after deployment\n"
|
||||
print "\nRun this tool such as:\n"
|
||||
print "./create_agent.py -f TA-metricator-hec-for-nmon_xxx.tgz --agentname TA-metricator-hec-for-nmon-custom \n"
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Will expect in first Argument the name of the tgz Archive of the Application to be downloaded in Splunk Base
|
||||
if not args.INFILE:
|
||||
print "\nERROR: Please provide the tgz Archive file with -f statement\n"
|
||||
sys.exit(1)
|
||||
else:
|
||||
infile = args.INFILE
|
||||
|
||||
# If the root directory of the TA-nmon is not defined, exit and show message
|
||||
if not args.TARGET:
|
||||
print "ERROR: You must specify the name of the agent package you want to create, and it must be different from" \
|
||||
" the default package: TA-metricator-hec-for-nmon"
|
||||
sys.exit(0)
|
||||
else:
|
||||
ta_root_dir = args.TARGET
|
||||
|
||||
# Avoid naming the TA ascore application
|
||||
if not "TA-" in ta_root_dir:
|
||||
print "ERROR: The TA package name should always start by TA_ as good Splunk practice."
|
||||
sys.exit(1)
|
||||
|
||||
# Verify tgz Archive file exists
|
||||
if not os.path.exists(infile):
|
||||
print ('ERROR: invalid file, could not find: ' + infile)
|
||||
sys.exit(1)
|
||||
|
||||
# Ensure the same package name does not already exist in current directory
|
||||
if os.path.exists(ta_root_dir):
|
||||
print ('ERROR: A directory named ' + ta_root_dir + ' already exist in current directory, please remove it and'
|
||||
' restart')
|
||||
sys.exit(1)
|
||||
elif os.path.exists(ta_root_dir + ".tgz"):
|
||||
print ('ERROR: A tgz archive named ' + ta_root_dir + ".tgz" + ' already exist in current directory, please'
|
||||
' remove it and restart')
|
||||
sys.exit(1)
|
||||
|
||||
# Extract Archive
|
||||
tar = tarfile.open(infile)
|
||||
msg = 'Extracting tgz Archive: ' + infile
|
||||
print (msg)
|
||||
tar.extractall()
|
||||
tar.close()
|
||||
|
||||
# Operate
|
||||
|
||||
# Get current directory
|
||||
curdir = os.getcwd()
|
||||
|
||||
# Extract the TA-nmon default package in current directory
|
||||
|
||||
print ('INFO: Extracting Agent tgz resources Archives')
|
||||
|
||||
tgz_files = 'TA-metricator-hec-for-nmon*.tgz'
|
||||
for tgz in glob.glob(str(tgz_files)):
|
||||
tar = tarfile.open(tgz)
|
||||
tar.extractall()
|
||||
tar.close()
|
||||
|
||||
# Rename the TA directory to match agent name
|
||||
|
||||
msg = 'INFO: Renaming TA-metricator-hec-for-nmon default agent to ' + ta_root_dir
|
||||
print (msg)
|
||||
|
||||
shutil.copytree('TA-metricator-hec-for-nmon', ta_root_dir)
|
||||
|
||||
################# STRING REPLACEMENTS #################
|
||||
|
||||
# Replace the old agent name in files
|
||||
|
||||
# Achieve string replacements
|
||||
|
||||
print ('Achieving files transformation...')
|
||||
|
||||
search = 'TA-metricator-hec-for-nmon'
|
||||
replace = ta_root_dir
|
||||
findreplace(ta_root_dir, search, replace, "*.sh")
|
||||
findreplace(ta_root_dir, search, replace, "*.py")
|
||||
findreplace(ta_root_dir, search, replace, "*.pl")
|
||||
findreplace(ta_root_dir, search, replace, "*.conf")
|
||||
|
||||
print ('Done.')
|
||||
|
||||
# Don't use "with" statement in tar creation for Python 2.6 backward compatibility
|
||||
tar_file = ta_root_dir + '.tgz'
|
||||
out = tarfile.open(tar_file, mode='w:gz')
|
||||
|
||||
try:
|
||||
out.add(ta_root_dir)
|
||||
finally:
|
||||
msg = 'INFO: ************* Tar creation done of: ' + tar_file + ' *************'
|
||||
print (msg)
|
||||
out.close()
|
||||
|
||||
# remove Agent directory
|
||||
if os.path.isdir(ta_root_dir):
|
||||
shutil.rmtree(ta_root_dir)
|
||||
|
||||
print ('\n*** Agent Creation terminated: To install the agent: ***\n')
|
||||
print (' - Upload the tgz Archive ' + tar_file + ' to your Splunk deployment server')
|
||||
print (' - Extract the content of the TA package in $SPLUNK_HOME/etc/deployment-apps/')
|
||||
print (' - Configure the Application (set splunkd to restart), server class and associated clients to push the new'
|
||||
' package to your clients\n')
|
||||
|
||||
# END
|
||||
print ('Operation terminated.\n')
|
||||
sys.exit(0)
|
||||
@ -0,0 +1,3 @@
|
||||
Text-CSV-1.95: http://search.cpan.org/~ishigaki/Text-CSV-1.95/lib/Text/CSV.pm
|
||||
|
||||
Compiled on AIX 7.1, certified under AIX 7.1 and 7.2
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,745 @@
|
||||
package Text::Diff;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Carp qw/ croak confess /;
|
||||
use Exporter ();
|
||||
use Algorithm::Diff ();
|
||||
|
||||
our $VERSION = '1.45';
|
||||
our @ISA = qw/ Exporter /;
|
||||
our @EXPORT = qw/ diff /;
|
||||
|
||||
## Hunks are made of ops. An op is the starting index for each
|
||||
## sequence and the opcode:
|
||||
use constant A => 0; # Array index before match/discard
|
||||
use constant B => 1;
|
||||
use constant OPCODE => 2; # "-", " ", "+"
|
||||
use constant FLAG => 3; # What to display if not OPCODE "!"
|
||||
|
||||
my %internal_styles = (
|
||||
Unified => undef,
|
||||
Context => undef,
|
||||
OldStyle => undef,
|
||||
Table => undef, ## "internal", but in another module
|
||||
);
|
||||
|
||||
sub diff {
|
||||
my @seqs = ( shift, shift );
|
||||
my $options = shift || {};
|
||||
|
||||
for my $i ( 0 .. 1 ) {
|
||||
my $seq = $seqs[$i];
|
||||
my $type = ref $seq;
|
||||
|
||||
while ( $type eq "CODE" ) {
|
||||
$seqs[$i] = $seq = $seq->( $options );
|
||||
$type = ref $seq;
|
||||
}
|
||||
|
||||
my $AorB = !$i ? "A" : "B";
|
||||
|
||||
if ( $type eq "ARRAY" ) {
|
||||
## This is most efficient :)
|
||||
$options->{"OFFSET_$AorB"} = 0
|
||||
unless defined $options->{"OFFSET_$AorB"};
|
||||
}
|
||||
elsif ( $type eq "SCALAR" ) {
|
||||
$seqs[$i] = [split( /^/m, $$seq )];
|
||||
$options->{"OFFSET_$AorB"} = 1
|
||||
unless defined $options->{"OFFSET_$AorB"};
|
||||
}
|
||||
elsif ( ! $type ) {
|
||||
$options->{"OFFSET_$AorB"} = 1
|
||||
unless defined $options->{"OFFSET_$AorB"};
|
||||
$options->{"FILENAME_$AorB"} = $seq
|
||||
unless defined $options->{"FILENAME_$AorB"};
|
||||
$options->{"MTIME_$AorB"} = (stat($seq))[9]
|
||||
unless defined $options->{"MTIME_$AorB"};
|
||||
|
||||
local $/ = "\n";
|
||||
open F, "<$seq" or croak "$!: $seq";
|
||||
$seqs[$i] = [<F>];
|
||||
close F;
|
||||
|
||||
}
|
||||
elsif ( $type eq "GLOB" || UNIVERSAL::isa( $seq, "IO::Handle" ) ) {
|
||||
$options->{"OFFSET_$AorB"} = 1
|
||||
unless defined $options->{"OFFSET_$AorB"};
|
||||
local $/ = "\n";
|
||||
$seqs[$i] = [<$seq>];
|
||||
}
|
||||
else {
|
||||
confess "Can't handle input of type ", ref;
|
||||
}
|
||||
}
|
||||
|
||||
## Config vars
|
||||
my $output;
|
||||
my $output_handler = $options->{OUTPUT};
|
||||
my $type = ref $output_handler ;
|
||||
if ( ! defined $output_handler ) {
|
||||
$output = "";
|
||||
$output_handler = sub { $output .= shift };
|
||||
}
|
||||
elsif ( $type eq "CODE" ) {
|
||||
## No problems, mate.
|
||||
}
|
||||
elsif ( $type eq "SCALAR" ) {
|
||||
my $out_ref = $output_handler;
|
||||
$output_handler = sub { $$out_ref .= shift };
|
||||
}
|
||||
elsif ( $type eq "ARRAY" ) {
|
||||
my $out_ref = $output_handler;
|
||||
$output_handler = sub { push @$out_ref, shift };
|
||||
}
|
||||
elsif ( $type eq "GLOB" || UNIVERSAL::isa $output_handler, "IO::Handle" ) {
|
||||
my $output_handle = $output_handler;
|
||||
$output_handler = sub { print $output_handle shift };
|
||||
}
|
||||
else {
|
||||
croak "Unrecognized output type: $type";
|
||||
}
|
||||
|
||||
my $style = $options->{STYLE};
|
||||
$style = "Unified" unless defined $options->{STYLE};
|
||||
$style = "Text::Diff::$style" if exists $internal_styles{$style};
|
||||
|
||||
if ( ! $style->can( "hunk" ) ) {
|
||||
eval "require $style; 1" or die $@;
|
||||
}
|
||||
|
||||
$style = $style->new if ! ref $style && $style->can( "new" );
|
||||
|
||||
my $ctx_lines = $options->{CONTEXT};
|
||||
$ctx_lines = 3 unless defined $ctx_lines;
|
||||
$ctx_lines = 0 if $style->isa( "Text::Diff::OldStyle" );
|
||||
|
||||
my @keygen_args = $options->{KEYGEN_ARGS}
|
||||
? @{$options->{KEYGEN_ARGS}}
|
||||
: ();
|
||||
|
||||
## State vars
|
||||
my $diffs = 0; ## Number of discards this hunk
|
||||
my $ctx = 0; ## Number of " " (ctx_lines) ops pushed after last diff.
|
||||
my @ops; ## ops (" ", +, -) in this hunk
|
||||
my $hunks = 0; ## Number of hunks
|
||||
|
||||
my $emit_ops = sub {
|
||||
$output_handler->( $style->file_header( @seqs, $options ) )
|
||||
unless $hunks++;
|
||||
$output_handler->( $style->hunk_header( @seqs, @_, $options ) );
|
||||
$output_handler->( $style->hunk ( @seqs, @_, $options ) );
|
||||
$output_handler->( $style->hunk_footer( @seqs, @_, $options ) );
|
||||
};
|
||||
|
||||
## We keep 2*ctx_lines so that if a diff occurs
|
||||
## at 2*ctx_lines we continue to grow the hunk instead
|
||||
## of emitting diffs and context as we go. We
|
||||
## need to know the total length of both of the two
|
||||
## subsequences so the line count can be printed in the
|
||||
## header.
|
||||
my $dis_a = sub {push @ops, [@_[0,1],"-"]; ++$diffs ; $ctx = 0 };
|
||||
my $dis_b = sub {push @ops, [@_[0,1],"+"]; ++$diffs ; $ctx = 0 };
|
||||
|
||||
Algorithm::Diff::traverse_sequences(
|
||||
@seqs,
|
||||
{
|
||||
MATCH => sub {
|
||||
push @ops, [@_[0,1]," "];
|
||||
|
||||
if ( $diffs && ++$ctx > $ctx_lines * 2 ) {
|
||||
$emit_ops->( [ splice @ops, 0, $#ops - $ctx_lines ] );
|
||||
$ctx = $diffs = 0;
|
||||
}
|
||||
|
||||
## throw away context lines that aren't needed any more
|
||||
shift @ops if ! $diffs && @ops > $ctx_lines;
|
||||
},
|
||||
DISCARD_A => $dis_a,
|
||||
DISCARD_B => $dis_b,
|
||||
},
|
||||
$options->{KEYGEN}, # pass in user arguments for key gen function
|
||||
@keygen_args,
|
||||
);
|
||||
|
||||
if ( $diffs ) {
|
||||
$#ops -= $ctx - $ctx_lines if $ctx > $ctx_lines;
|
||||
$emit_ops->( \@ops );
|
||||
}
|
||||
|
||||
$output_handler->( $style->file_footer( @seqs, $options ) ) if $hunks;
|
||||
|
||||
return defined $output ? $output : $hunks;
|
||||
}
|
||||
|
||||
sub _header {
|
||||
my ( $h ) = @_;
|
||||
my ( $p1, $fn1, $t1, $p2, $fn2, $t2 ) = @{$h}{
|
||||
"FILENAME_PREFIX_A",
|
||||
"FILENAME_A",
|
||||
"MTIME_A",
|
||||
"FILENAME_PREFIX_B",
|
||||
"FILENAME_B",
|
||||
"MTIME_B"
|
||||
};
|
||||
|
||||
## remember to change Text::Diff::Table if this logic is tweaked.
|
||||
return "" unless defined $fn1 && defined $fn2;
|
||||
|
||||
return join( "",
|
||||
$p1, " ", $fn1, defined $t1 ? "\t" . localtime $t1 : (), "\n",
|
||||
$p2, " ", $fn2, defined $t2 ? "\t" . localtime $t2 : (), "\n",
|
||||
);
|
||||
}
|
||||
|
||||
## _range encapsulates the building of, well, ranges. Turns out there are
|
||||
## a few nuances.
|
||||
sub _range {
|
||||
my ( $ops, $a_or_b, $format ) = @_;
|
||||
|
||||
my $start = $ops->[ 0]->[$a_or_b];
|
||||
my $after = $ops->[-1]->[$a_or_b];
|
||||
|
||||
## The sequence indexes in the lines are from *before* the OPCODE is
|
||||
## executed, so we bump the last index up unless the OP indicates
|
||||
## it didn't change.
|
||||
++$after
|
||||
unless $ops->[-1]->[OPCODE] eq ( $a_or_b == A ? "+" : "-" );
|
||||
|
||||
## convert from 0..n index to 1..(n+1) line number. The unless modifier
|
||||
## handles diffs with no context, where only one file is affected. In this
|
||||
## case $start == $after indicates an empty range, and the $start must
|
||||
## not be incremented.
|
||||
my $empty_range = $start == $after;
|
||||
++$start unless $empty_range;
|
||||
|
||||
return
|
||||
$start == $after
|
||||
? $format eq "unified" && $empty_range
|
||||
? "$start,0"
|
||||
: $start
|
||||
: $format eq "unified"
|
||||
? "$start,".($after-$start+1)
|
||||
: "$start,$after";
|
||||
}
|
||||
|
||||
sub _op_to_line {
|
||||
my ( $seqs, $op, $a_or_b, $op_prefixes ) = @_;
|
||||
|
||||
my $opcode = $op->[OPCODE];
|
||||
return () unless defined $op_prefixes->{$opcode};
|
||||
|
||||
my $op_sym = defined $op->[FLAG] ? $op->[FLAG] : $opcode;
|
||||
$op_sym = $op_prefixes->{$op_sym};
|
||||
return () unless defined $op_sym;
|
||||
|
||||
$a_or_b = $op->[OPCODE] ne "+" ? 0 : 1 unless defined $a_or_b;
|
||||
my @line = ( $op_sym, $seqs->[$a_or_b][$op->[$a_or_b]] );
|
||||
unless ( $line[1] =~ /(?:\n|\r\n)$/ ) {
|
||||
$line[1] .= "\n\\ No newline at end of file\n";
|
||||
}
|
||||
return @line;
|
||||
}
|
||||
|
||||
SCOPE: {
|
||||
package Text::Diff::Base;
|
||||
|
||||
sub new {
|
||||
my $proto = shift;
|
||||
return bless { @_ }, ref $proto || $proto;
|
||||
}
|
||||
|
||||
sub file_header { return "" }
|
||||
|
||||
sub hunk_header { return "" }
|
||||
|
||||
sub hunk { return "" }
|
||||
|
||||
sub hunk_footer { return "" }
|
||||
|
||||
sub file_footer { return "" }
|
||||
}
|
||||
|
||||
@Text::Diff::Unified::ISA = qw( Text::Diff::Base );
|
||||
|
||||
sub Text::Diff::Unified::file_header {
|
||||
shift; ## No instance data
|
||||
my $options = pop ;
|
||||
|
||||
_header(
|
||||
{ FILENAME_PREFIX_A => "---", FILENAME_PREFIX_B => "+++", %$options }
|
||||
);
|
||||
}
|
||||
|
||||
sub Text::Diff::Unified::hunk_header {
|
||||
shift; ## No instance data
|
||||
pop; ## Ignore options
|
||||
my $ops = pop;
|
||||
|
||||
return join( "",
|
||||
"@@ -",
|
||||
_range( $ops, A, "unified" ),
|
||||
" +",
|
||||
_range( $ops, B, "unified" ),
|
||||
" @@\n",
|
||||
);
|
||||
}
|
||||
|
||||
sub Text::Diff::Unified::hunk {
|
||||
shift; ## No instance data
|
||||
pop; ## Ignore options
|
||||
my $ops = pop;
|
||||
|
||||
my $prefixes = { "+" => "+", " " => " ", "-" => "-" };
|
||||
|
||||
return join "", map _op_to_line( \@_, $_, undef, $prefixes ), @$ops
|
||||
}
|
||||
|
||||
@Text::Diff::Context::ISA = qw( Text::Diff::Base );
|
||||
|
||||
sub Text::Diff::Context::file_header {
|
||||
_header { FILENAME_PREFIX_A=>"***", FILENAME_PREFIX_B=>"---", %{$_[-1]} };
|
||||
}
|
||||
|
||||
sub Text::Diff::Context::hunk_header {
|
||||
return "***************\n";
|
||||
}
|
||||
|
||||
sub Text::Diff::Context::hunk {
|
||||
shift; ## No instance data
|
||||
pop; ## Ignore options
|
||||
my $ops = pop;
|
||||
## Leave the sequences in @_[0,1]
|
||||
|
||||
my $a_range = _range( $ops, A, "" );
|
||||
my $b_range = _range( $ops, B, "" );
|
||||
|
||||
## Sigh. Gotta make sure that differences that aren't adds/deletions
|
||||
## get prefixed with "!", and that the old opcodes are removed.
|
||||
my $after;
|
||||
for ( my $start = 0; $start <= $#$ops ; $start = $after ) {
|
||||
## Scan until next difference
|
||||
$after = $start + 1;
|
||||
my $opcode = $ops->[$start]->[OPCODE];
|
||||
next if $opcode eq " ";
|
||||
|
||||
my $bang_it;
|
||||
while ( $after <= $#$ops && $ops->[$after]->[OPCODE] ne " " ) {
|
||||
$bang_it ||= $ops->[$after]->[OPCODE] ne $opcode;
|
||||
++$after;
|
||||
}
|
||||
|
||||
if ( $bang_it ) {
|
||||
for my $i ( $start..($after-1) ) {
|
||||
$ops->[$i]->[FLAG] = "!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $b_prefixes = { "+" => "+ ", " " => " ", "-" => undef, "!" => "! " };
|
||||
my $a_prefixes = { "+" => undef, " " => " ", "-" => "- ", "!" => "! " };
|
||||
|
||||
return join( "",
|
||||
"*** ", $a_range, " ****\n",
|
||||
map( _op_to_line( \@_, $_, A, $a_prefixes ), @$ops ),
|
||||
"--- ", $b_range, " ----\n",
|
||||
map( _op_to_line( \@_, $_, B, $b_prefixes ), @$ops ),
|
||||
);
|
||||
}
|
||||
|
||||
@Text::Diff::OldStyle::ISA = qw( Text::Diff::Base );
|
||||
|
||||
sub _op {
|
||||
my $ops = shift;
|
||||
my $op = $ops->[0]->[OPCODE];
|
||||
$op = "c" if grep $_->[OPCODE] ne $op, @$ops;
|
||||
$op = "a" if $op eq "+";
|
||||
$op = "d" if $op eq "-";
|
||||
return $op;
|
||||
}
|
||||
|
||||
sub Text::Diff::OldStyle::hunk_header {
|
||||
shift; ## No instance data
|
||||
pop; ## ignore options
|
||||
my $ops = pop;
|
||||
|
||||
my $op = _op $ops;
|
||||
|
||||
return join "", _range( $ops, A, "" ), $op, _range( $ops, B, "" ), "\n";
|
||||
}
|
||||
|
||||
sub Text::Diff::OldStyle::hunk {
|
||||
shift; ## No instance data
|
||||
pop; ## ignore options
|
||||
my $ops = pop;
|
||||
## Leave the sequences in @_[0,1]
|
||||
|
||||
my $a_prefixes = { "+" => undef, " " => undef, "-" => "< " };
|
||||
my $b_prefixes = { "+" => "> ", " " => undef, "-" => undef };
|
||||
|
||||
my $op = _op $ops;
|
||||
|
||||
return join( "",
|
||||
map( _op_to_line( \@_, $_, A, $a_prefixes ), @$ops ),
|
||||
$op eq "c" ? "---\n" : (),
|
||||
map( _op_to_line( \@_, $_, B, $b_prefixes ), @$ops ),
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Text::Diff - Perform diffs on files and record sets
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Text::Diff;
|
||||
|
||||
## Mix and match filenames, strings, file handles, producer subs,
|
||||
## or arrays of records; returns diff in a string.
|
||||
## WARNING: can return B<large> diffs for large files.
|
||||
my $diff = diff "file1.txt", "file2.txt", { STYLE => "Context" };
|
||||
my $diff = diff \$string1, \$string2, \%options;
|
||||
my $diff = diff \*FH1, \*FH2;
|
||||
my $diff = diff \&reader1, \&reader2;
|
||||
my $diff = diff \@records1, \@records2;
|
||||
|
||||
## May also mix input types:
|
||||
my $diff = diff \@records1, "file_B.txt";
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
C<diff()> provides a basic set of services akin to the GNU C<diff> utility. It
|
||||
is not anywhere near as feature complete as GNU C<diff>, but it is better
|
||||
integrated with Perl and available on all platforms. It is often faster than
|
||||
shelling out to a system's C<diff> executable for small files, and generally
|
||||
slower on larger files.
|
||||
|
||||
Relies on L<Algorithm::Diff> for, well, the algorithm. This may not produce
|
||||
the same exact diff as a system's local C<diff> executable, but it will be a
|
||||
valid diff and comprehensible by C<patch>. We haven't seen any differences
|
||||
between L<Algorithm::Diff>'s logic and GNU C<diff>'s, but we have not examined
|
||||
them to make sure they are indeed identical.
|
||||
|
||||
B<Note>: If you don't want to import the C<diff> function, do one of the
|
||||
following:
|
||||
|
||||
use Text::Diff ();
|
||||
|
||||
require Text::Diff;
|
||||
|
||||
That's a pretty rare occurrence,
|
||||
so C<diff()> is exported by default.
|
||||
|
||||
If you pass a filename, but the file can't be read,
|
||||
then C<diff()> will C<croak>.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
C<diff()> takes two parameters from which to draw input and a set of
|
||||
options to control its output. The options are:
|
||||
|
||||
=over
|
||||
|
||||
=item FILENAME_A, MTIME_A, FILENAME_B, MTIME_B
|
||||
|
||||
The name of the file and the modification time "files".
|
||||
|
||||
These are filled in automatically for each file when C<diff()> is passed a
|
||||
filename, unless a defined value is passed in.
|
||||
|
||||
If a filename is not passed in and FILENAME_A and FILENAME_B are not provided
|
||||
or are C<undef>, the header will not be printed.
|
||||
|
||||
Unused on C<OldStyle> diffs.
|
||||
|
||||
=item OFFSET_A, OFFSET_B
|
||||
|
||||
The index of the first line / element. These default to 1 for all
|
||||
parameter types except ARRAY references, for which the default is 0. This
|
||||
is because ARRAY references are presumed to be data structures, while the
|
||||
others are line-oriented text.
|
||||
|
||||
=item STYLE
|
||||
|
||||
"Unified", "Context", "OldStyle", or an object or class reference for a class
|
||||
providing C<file_header()>, C<hunk_header()>, C<hunk()>, C<hunk_footer()> and
|
||||
C<file_footer()> methods. The two footer() methods are provided for
|
||||
overloading only; none of the formats provide them.
|
||||
|
||||
Defaults to "Unified" (unlike standard C<diff>, but Unified is what's most
|
||||
often used in submitting patches and is the most human readable of the three.
|
||||
|
||||
If the package indicated by the STYLE has no C<hunk()> method, C<diff()> will
|
||||
load it automatically (lazy loading). Since all such packages should inherit
|
||||
from C<Text::Diff::Base>, this should be marvy.
|
||||
|
||||
Styles may be specified as class names (C<STYLE =E<gt> 'Foo'>),
|
||||
in which case they will be C<new()>ed with no parameters,
|
||||
or as objects (C<STYLE =E<gt> Foo-E<gt>new>).
|
||||
|
||||
=item CONTEXT
|
||||
|
||||
How many lines before and after each diff to display. Ignored on old-style
|
||||
diffs. Defaults to 3.
|
||||
|
||||
=item OUTPUT
|
||||
|
||||
Examples and their equivalent subroutines:
|
||||
|
||||
OUTPUT => \*FOOHANDLE, # like: sub { print FOOHANDLE shift() }
|
||||
OUTPUT => \$output, # like: sub { $output .= shift }
|
||||
OUTPUT => \@output, # like: sub { push @output, shift }
|
||||
OUTPUT => sub { $output .= shift },
|
||||
|
||||
If no C<OUTPUT> is supplied, returns the diffs in a string. If
|
||||
C<OUTPUT> is a C<CODE> ref, it will be called once with the (optional)
|
||||
file header, and once for each hunk body with the text to emit. If
|
||||
C<OUTPUT> is an L<IO::Handle>, output will be emitted to that handle.
|
||||
|
||||
=item FILENAME_PREFIX_A, FILENAME_PREFIX_B
|
||||
|
||||
The string to print before the filename in the header. Unused on C<OldStyle>
|
||||
diffs. Defaults are C<"---">, C<"+++"> for Unified and C<"***">, C<"+++"> for
|
||||
Context.
|
||||
|
||||
=item KEYGEN, KEYGEN_ARGS
|
||||
|
||||
These are passed to L<Algorithm::Diff/traverse_sequences>.
|
||||
|
||||
=back
|
||||
|
||||
B<Note>: if neither C<FILENAME_> option is defined, the header will not be
|
||||
printed. If at least one is present, the other and both C<MTIME_> options must
|
||||
be present or "Use of undefined variable" warnings will be generated (except
|
||||
on C<OldStyle> diffs, which ignores these options).
|
||||
|
||||
=head1 Formatting Classes
|
||||
|
||||
These functions implement the output formats. They are grouped in to classes
|
||||
so C<diff()> can use class names to call the correct set of output routines and
|
||||
so that you may inherit from them easily. There are no constructors or
|
||||
instance methods for these classes, though subclasses may provide them if need
|
||||
be.
|
||||
|
||||
Each class has C<file_header()>, C<hunk_header()>, C<hunk()>, and C<footer()>
|
||||
methods identical to those documented in the C<Text::Diff::Unified> section.
|
||||
C<header()> is called before the C<hunk()> is first called, C<footer()>
|
||||
afterwards. The default footer function is an empty method provided for
|
||||
overloading:
|
||||
|
||||
sub footer { return "End of patch\n" }
|
||||
|
||||
Some output formats are provided by external modules (which are loaded
|
||||
automatically), such as L<Text::Diff::Table>. These are
|
||||
are documented here to keep the documentation simple.
|
||||
|
||||
=head2 Text::Diff::Base
|
||||
|
||||
Returns "" for all methods (other than C<new()>).
|
||||
|
||||
=head2 Text::Diff::Unified
|
||||
|
||||
--- A Mon Nov 12 23:49:30 2001
|
||||
+++ B Mon Nov 12 23:49:30 2001
|
||||
@@ -2,13 +2,13 @@
|
||||
2
|
||||
3
|
||||
4
|
||||
-5d
|
||||
+5a
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
+9a
|
||||
10
|
||||
11
|
||||
-11d
|
||||
12
|
||||
13
|
||||
|
||||
=over
|
||||
|
||||
=item Text::Diff::Unified::file_header
|
||||
|
||||
$s = Text::Diff::Unified->file_header( $options );
|
||||
|
||||
Returns a string containing a unified header. The sole parameter is the
|
||||
C<options> hash passed in to C<diff()>, containing at least:
|
||||
|
||||
FILENAME_A => $fn1,
|
||||
MTIME_A => $mtime1,
|
||||
FILENAME_B => $fn2,
|
||||
MTIME_B => $mtime2
|
||||
|
||||
May also contain
|
||||
|
||||
FILENAME_PREFIX_A => "---",
|
||||
FILENAME_PREFIX_B => "+++",
|
||||
|
||||
to override the default prefixes (default values shown).
|
||||
|
||||
=item Text::Diff::Unified::hunk_header
|
||||
|
||||
Text::Diff::Unified->hunk_header( \@ops, $options );
|
||||
|
||||
Returns a string containing the heading of one hunk of unified diff.
|
||||
|
||||
=item Text::Diff::Unified::hunk
|
||||
|
||||
Text::Diff::Unified->hunk( \@seq_a, \@seq_b, \@ops, $options );
|
||||
|
||||
Returns a string containing the output of one hunk of unified diff.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Text::Diff::Table
|
||||
|
||||
+--+----------------------------------+--+------------------------------+
|
||||
| |../Test-Differences-0.2/MANIFEST | |../Test-Differences/MANIFEST |
|
||||
| |Thu Dec 13 15:38:49 2001 | |Sat Dec 15 02:09:44 2001 |
|
||||
+--+----------------------------------+--+------------------------------+
|
||||
| | * 1|Changes *
|
||||
| 1|Differences.pm | 2|Differences.pm |
|
||||
| 2|MANIFEST | 3|MANIFEST |
|
||||
| | * 4|MANIFEST.SKIP *
|
||||
| 3|Makefile.PL | 5|Makefile.PL |
|
||||
| | * 6|t/00escape.t *
|
||||
| 4|t/00flatten.t | 7|t/00flatten.t |
|
||||
| 5|t/01text_vs_data.t | 8|t/01text_vs_data.t |
|
||||
| 6|t/10test.t | 9|t/10test.t |
|
||||
+--+----------------------------------+--+------------------------------+
|
||||
|
||||
This format also goes to some pains to highlight "invisible" characters on
|
||||
differing elements by selectively escaping whitespace:
|
||||
|
||||
+--+--------------------------+--------------------------+
|
||||
| |demo_ws_A.txt |demo_ws_B.txt |
|
||||
| |Fri Dec 21 08:36:32 2001 |Fri Dec 21 08:36:50 2001 |
|
||||
+--+--------------------------+--------------------------+
|
||||
| 1|identical |identical |
|
||||
* 2| spaced in | also spaced in *
|
||||
* 3|embedded space |embedded tab *
|
||||
| 4|identical |identical |
|
||||
* 5| spaced in |\ttabbed in *
|
||||
* 6|trailing spaces\s\s\n |trailing tabs\t\t\n *
|
||||
| 7|identical |identical |
|
||||
* 8|lf line\n |crlf line\r\n *
|
||||
* 9|embedded ws |embedded\tws *
|
||||
+--+--------------------------+--------------------------+
|
||||
|
||||
See L<Text::Diff::Table> for more details, including how the whitespace
|
||||
escaping works.
|
||||
|
||||
=head2 Text::Diff::Context
|
||||
|
||||
*** A Mon Nov 12 23:49:30 2001
|
||||
--- B Mon Nov 12 23:49:30 2001
|
||||
***************
|
||||
*** 2,14 ****
|
||||
2
|
||||
3
|
||||
4
|
||||
! 5d
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
||||
11
|
||||
- 11d
|
||||
12
|
||||
13
|
||||
--- 2,14 ----
|
||||
2
|
||||
3
|
||||
4
|
||||
! 5a
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
+ 9a
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
|
||||
Note: C<hunk_header()> returns only "***************\n".
|
||||
|
||||
=head2 Text::Diff::OldStyle
|
||||
|
||||
5c5
|
||||
< 5d
|
||||
---
|
||||
> 5a
|
||||
9a10
|
||||
> 9a
|
||||
12d12
|
||||
< 11d
|
||||
|
||||
Note: no C<file_header()>.
|
||||
|
||||
=head1 LIMITATIONS
|
||||
|
||||
Must suck both input files entirely in to memory and store them with a normal
|
||||
amount of Perlish overhead (one array location) per record. This is implied by
|
||||
the implementation of L<Algorithm::Diff>, which takes two arrays. If
|
||||
L<Algorithm::Diff> ever offers an incremental mode, this can be changed
|
||||
(contact the maintainers of L<Algorithm::Diff> and C<Text::Diff> if you need
|
||||
this; it shouldn't be too terribly hard to tie arrays in this fashion).
|
||||
|
||||
Does not provide most of the more refined GNU C<diff> options: recursive
|
||||
directory tree scanning, ignoring blank lines / whitespace, etc., etc. These
|
||||
can all be added as time permits and need arises, many are rather easy; patches
|
||||
quite welcome.
|
||||
|
||||
Uses closures internally, this may lead to leaks on Perl versions 5.6.1 and
|
||||
prior if used many times over a process' life time.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Algorithm::Diff> - the underlying implementation of the diff algorithm
|
||||
used by C<Text::Diff>.
|
||||
|
||||
L<YAML::Diff> - find difference between two YAML documents.
|
||||
|
||||
L<HTML::Differences> - find difference between two HTML documents.
|
||||
This uses a more sane approach than L<HTML::Diff>.
|
||||
|
||||
L<XML::Diff> - find difference between two XML documents.
|
||||
|
||||
L<Array::Diff> - find the differences between two Perl arrays.
|
||||
|
||||
L<Hash::Diff> - find the differences between two Perl hashes.
|
||||
|
||||
L<Data::Diff> - find difference between two arbitrary data structures.
|
||||
|
||||
=head1 REPOSITORY
|
||||
|
||||
L<https://github.com/neilbowers/Text-Diff>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
|
||||
|
||||
Barrie Slaymaker E<lt>barries@slaysys.comE<gt>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Some parts copyright 2009 Adam Kennedy.
|
||||
|
||||
Copyright 2001 Barrie Slaymaker. All Rights Reserved.
|
||||
|
||||
You may use this under the terms of either the Artistic License or GNU Public
|
||||
License v 2.0 or greater.
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
@ -0,0 +1,142 @@
|
||||
package Text::Diff::Config;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '1.44';
|
||||
our $Output_Unicode;
|
||||
|
||||
BEGIN
|
||||
{
|
||||
$Output_Unicode = $ENV{'DIFF_OUTPUT_UNICODE'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Text::Diff::Config - global configuration for Text::Diff (as a
|
||||
separate module).
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Text::Diff::Config;
|
||||
|
||||
$Text::Diff::Config::Output_Unicode = 1;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module configures Text::Diff and its related modules. Currently it contains
|
||||
only one global variable $Text::Diff::Config::Output_Unicode which is a boolean
|
||||
flag, that if set outputs unicode characters as themselves without escaping them
|
||||
as C< \x{HHHH} > first.
|
||||
|
||||
It is initialized to the value of C< $ENV{DIFF_OUTPUT_UNICODE} >, but can be
|
||||
set to a different value at run-time, including using local.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Shlomi Fish, L<http://www.shlomifish.org/> .
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
Copyright 2010, Shlomi Fish.
|
||||
|
||||
This file is licensed under the MIT/X11 License:
|
||||
L<http://www.opensource.org/licenses/mit-license.php>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
=cut
|
||||
|
||||
package Text::Diff::Config;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use vars qw($Output_Unicode);
|
||||
|
||||
BEGIN
|
||||
{
|
||||
$Output_Unicode = $ENV{'DIFF_OUTPUT_UNICODE'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Text::Diff::Config - global configuration for Text::Diff (as a
|
||||
separate module).
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Text::Diff::Config;
|
||||
|
||||
$Text::Diff::Config::Output_Unicode = 1;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module configures Text::Diff and its related modules. Currently it contains
|
||||
only one global variable $Text::Diff::Config::Output_Unicode which is a boolean
|
||||
flag, that if set outputs unicode characters as themselves without escaping them
|
||||
as C< \x{HHHH} > first.
|
||||
|
||||
It is initialized to the value of C< $ENV{DIFF_OUTPUT_UNICODE} >, but can be
|
||||
set to a different value at run-time, including using local.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Shlomi Fish, L<http://www.shlomifish.org/> .
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
Copyright 2010, Shlomi Fish.
|
||||
|
||||
This file is licensed under the MIT/X11 License:
|
||||
L<http://www.opensource.org/licenses/mit-license.php>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
=cut
|
||||
|
||||
@ -0,0 +1,429 @@
|
||||
package Text::Diff::Table;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Carp;
|
||||
use Text::Diff::Config;
|
||||
|
||||
our $VERSION = '1.44';
|
||||
our @ISA = qw( Text::Diff::Base Exporter );
|
||||
our @EXPORT_OK = qw( expand_tabs );
|
||||
|
||||
my %escapes = map {
|
||||
my $c =
|
||||
$_ eq '"' || $_ eq '$' ? qq{'$_'}
|
||||
: $_ eq "\\" ? qq{"\\\\"}
|
||||
: qq{"$_"};
|
||||
( ord eval $c => $_ )
|
||||
} (
|
||||
map( chr, 32..126),
|
||||
map( sprintf( "\\x%02x", $_ ), ( 0..31, 127..255 ) ),
|
||||
# map( "\\c$_", "A".."Z"),
|
||||
"\\t", "\\n", "\\r", "\\f", "\\b", "\\a", "\\e"
|
||||
## NOTE: "\\\\" is not here because some things are explicitly
|
||||
## escaped before escape() is called and we don't want to
|
||||
## double-escape "\". Also, in most texts, leaving "\" more
|
||||
## readable makes sense.
|
||||
);
|
||||
|
||||
sub expand_tabs($) {
|
||||
my $s = shift;
|
||||
my $count = 0;
|
||||
$s =~ s{(\t)(\t*)|([^\t]+)}{
|
||||
if ( $1 ) {
|
||||
my $spaces = " " x ( 8 - $count % 8 + 8 * length $2 );
|
||||
$count = 0;
|
||||
$spaces;
|
||||
}
|
||||
else {
|
||||
$count += length $3;
|
||||
$3;
|
||||
}
|
||||
}ge;
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
sub trim_trailing_line_ends($) {
|
||||
my $s = shift;
|
||||
$s =~ s/[\r\n]+(?!\n)$//;
|
||||
return $s;
|
||||
}
|
||||
|
||||
sub escape($);
|
||||
|
||||
SCOPE: {
|
||||
## use utf8 if available. don't if not.
|
||||
my $escaper = <<'EOCODE';
|
||||
sub escape($) {
|
||||
use utf8;
|
||||
join "", map {
|
||||
my $c = $_;
|
||||
$_ = ord;
|
||||
exists $escapes{$_}
|
||||
? $escapes{$_}
|
||||
: $Text::Diff::Config::Output_Unicode
|
||||
? $c
|
||||
: sprintf( "\\x{%04x}", $_ );
|
||||
} split //, shift;
|
||||
}
|
||||
|
||||
1;
|
||||
EOCODE
|
||||
unless ( eval $escaper ) {
|
||||
$escaper =~ s/ *use *utf8 *;\n// or die "Can't drop use utf8;";
|
||||
eval $escaper or die $@;
|
||||
}
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $proto = shift;
|
||||
return bless { @_ }, $proto
|
||||
}
|
||||
|
||||
my $missing_elt = [ "", "" ];
|
||||
|
||||
sub hunk {
|
||||
my $self = shift;
|
||||
my @seqs = ( shift, shift );
|
||||
my $ops = shift; ## Leave sequences in @_[0,1]
|
||||
my $options = shift;
|
||||
|
||||
my ( @A, @B );
|
||||
for ( @$ops ) {
|
||||
my $opcode = $_->[Text::Diff::OPCODE()];
|
||||
if ( $opcode eq " " ) {
|
||||
push @A, $missing_elt while @A < @B;
|
||||
push @B, $missing_elt while @B < @A;
|
||||
}
|
||||
push @A, [ $_->[0] + ( $options->{OFFSET_A} || 0), $seqs[0][$_->[0]] ]
|
||||
if $opcode eq " " || $opcode eq "-";
|
||||
push @B, [ $_->[1] + ( $options->{OFFSET_B} || 0), $seqs[1][$_->[1]] ]
|
||||
if $opcode eq " " || $opcode eq "+";
|
||||
}
|
||||
|
||||
push @A, $missing_elt while @A < @B;
|
||||
push @B, $missing_elt while @B < @A;
|
||||
my @elts;
|
||||
for ( 0..$#A ) {
|
||||
my ( $A, $B ) = (shift @A, shift @B );
|
||||
|
||||
## Do minimal cleaning on identical elts so these look "normal":
|
||||
## tabs are expanded, trailing newelts removed, etc. For differing
|
||||
## elts, make invisible characters visible if the invisible characters
|
||||
## differ.
|
||||
my $elt_type = $B == $missing_elt ? "A" :
|
||||
$A == $missing_elt ? "B" :
|
||||
$A->[1] eq $B->[1] ? "="
|
||||
: "*";
|
||||
if ( $elt_type ne "*" ) {
|
||||
if ( $elt_type eq "=" || $A->[1] =~ /\S/ || $B->[1] =~ /\S/ ) {
|
||||
$A->[1] = escape trim_trailing_line_ends expand_tabs $A->[1];
|
||||
$B->[1] = escape trim_trailing_line_ends expand_tabs $B->[1];
|
||||
}
|
||||
else {
|
||||
$A->[1] = escape $A->[1];
|
||||
$B->[1] = escape $B->[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
## not using \z here for backcompat reasons.
|
||||
$A->[1] =~ /^(\s*?)([^ \t].*?)?(\s*)(?![\n\r])$/s;
|
||||
my ( $l_ws_A, $body_A, $t_ws_A ) = ( $1, $2, $3 );
|
||||
$body_A = "" unless defined $body_A;
|
||||
$B->[1] =~ /^(\s*?)([^ \t].*?)?(\s*)(?![\n\r])$/s;
|
||||
my ( $l_ws_B, $body_B, $t_ws_B ) = ( $1, $2, $3 );
|
||||
$body_B = "" unless defined $body_B;
|
||||
|
||||
my $added_escapes;
|
||||
|
||||
if ( $l_ws_A ne $l_ws_B ) {
|
||||
## Make leading tabs visible. Other non-' ' chars
|
||||
## will be dealt with in escape(), but this prevents
|
||||
## tab expansion from hiding tabs by making them
|
||||
## look like ' '.
|
||||
$added_escapes = 1 if $l_ws_A =~ s/\t/\\t/g;
|
||||
$added_escapes = 1 if $l_ws_B =~ s/\t/\\t/g;
|
||||
}
|
||||
|
||||
if ( $t_ws_A ne $t_ws_B ) {
|
||||
## Only trailing whitespace gets the \s treatment
|
||||
## to make it obvious what's going on.
|
||||
$added_escapes = 1 if $t_ws_A =~ s/ /\\s/g;
|
||||
$added_escapes = 1 if $t_ws_B =~ s/ /\\s/g;
|
||||
$added_escapes = 1 if $t_ws_A =~ s/\t/\\t/g;
|
||||
$added_escapes = 1 if $t_ws_B =~ s/\t/\\t/g;
|
||||
}
|
||||
else {
|
||||
$t_ws_A = $t_ws_B = "";
|
||||
}
|
||||
|
||||
my $do_tab_escape = $added_escapes || do {
|
||||
my $expanded_A = expand_tabs join( $body_A, $l_ws_A, $t_ws_A );
|
||||
my $expanded_B = expand_tabs join( $body_B, $l_ws_B, $t_ws_B );
|
||||
$expanded_A eq $expanded_B;
|
||||
};
|
||||
|
||||
my $do_back_escape = $do_tab_escape || do {
|
||||
my ( $unescaped_A, $escaped_A,
|
||||
$unescaped_B, $escaped_B
|
||||
) =
|
||||
map
|
||||
join( "", /(\\.)/g ),
|
||||
map {
|
||||
( $_, escape $_ )
|
||||
}
|
||||
expand_tabs join( $body_A, $l_ws_A, $t_ws_A ),
|
||||
expand_tabs join( $body_B, $l_ws_B, $t_ws_B );
|
||||
$unescaped_A ne $unescaped_B && $escaped_A eq $escaped_B;
|
||||
};
|
||||
|
||||
if ( $do_back_escape ) {
|
||||
$body_A =~ s/\\/\\\\/g;
|
||||
$body_B =~ s/\\/\\\\/g;
|
||||
}
|
||||
|
||||
my $line_A = join $body_A, $l_ws_A, $t_ws_A;
|
||||
my $line_B = join $body_B, $l_ws_B, $t_ws_B;
|
||||
|
||||
unless ( $do_tab_escape ) {
|
||||
$line_A = expand_tabs $line_A;
|
||||
$line_B = expand_tabs $line_B;
|
||||
}
|
||||
|
||||
$A->[1] = escape $line_A;
|
||||
$B->[1] = escape $line_B;
|
||||
}
|
||||
|
||||
push @elts, [ @$A, @$B, $elt_type ];
|
||||
}
|
||||
|
||||
push @{$self->{ELTS}}, @elts, ["bar"];
|
||||
return "";
|
||||
}
|
||||
|
||||
sub _glean_formats {
|
||||
my $self = shift;
|
||||
}
|
||||
|
||||
sub file_footer {
|
||||
my $self = shift;
|
||||
my @seqs = (shift,shift);
|
||||
my $options = pop;
|
||||
|
||||
my @heading_lines;
|
||||
|
||||
if ( defined $options->{FILENAME_A} || defined $options->{FILENAME_B} ) {
|
||||
push @heading_lines, [
|
||||
map(
|
||||
{
|
||||
( "", escape( defined $_ ? $_ : "<undef>" ) );
|
||||
}
|
||||
( @{$options}{qw( FILENAME_A FILENAME_B)} )
|
||||
),
|
||||
"=",
|
||||
];
|
||||
}
|
||||
|
||||
if ( defined $options->{MTIME_A} || defined $options->{MTIME_B} ) {
|
||||
push @heading_lines, [
|
||||
map( {
|
||||
( "",
|
||||
escape(
|
||||
( defined $_ && length $_ )
|
||||
? localtime $_
|
||||
: ""
|
||||
)
|
||||
);
|
||||
}
|
||||
@{$options}{qw( MTIME_A MTIME_B )}
|
||||
),
|
||||
"=",
|
||||
];
|
||||
}
|
||||
|
||||
if ( defined $options->{INDEX_LABEL} ) {
|
||||
push @heading_lines, [ "", "", "", "", "=" ] unless @heading_lines;
|
||||
$heading_lines[-1]->[0] = $heading_lines[-1]->[2] =
|
||||
$options->{INDEX_LABEL};
|
||||
}
|
||||
|
||||
## Not ushifting on to @{$self->{ELTS}} in case it's really big. Want
|
||||
## to avoid the overhead.
|
||||
|
||||
my $four_column_mode = 0;
|
||||
for my $cols ( @heading_lines, @{$self->{ELTS}} ) {
|
||||
next if $cols->[-1] eq "bar";
|
||||
if ( $cols->[0] ne $cols->[2] ) {
|
||||
$four_column_mode = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
unless ( $four_column_mode ) {
|
||||
for my $cols ( @heading_lines, @{$self->{ELTS}} ) {
|
||||
next if $cols->[-1] eq "bar";
|
||||
splice @$cols, 2, 1;
|
||||
}
|
||||
}
|
||||
|
||||
my @w = (0,0,0,0);
|
||||
for my $cols ( @heading_lines, @{$self->{ELTS}} ) {
|
||||
next if $cols->[-1] eq "bar";
|
||||
for my $i (0..($#$cols-1)) {
|
||||
$w[$i] = length $cols->[$i]
|
||||
if defined $cols->[$i] && length $cols->[$i] > $w[$i];
|
||||
}
|
||||
}
|
||||
|
||||
my %fmts = $four_column_mode
|
||||
? (
|
||||
"=" => "| %$w[0]s|%-$w[1]s | %$w[2]s|%-$w[3]s |\n",
|
||||
"A" => "* %$w[0]s|%-$w[1]s * %$w[2]s|%-$w[3]s |\n",
|
||||
"B" => "| %$w[0]s|%-$w[1]s * %$w[2]s|%-$w[3]s *\n",
|
||||
"*" => "* %$w[0]s|%-$w[1]s * %$w[2]s|%-$w[3]s *\n",
|
||||
)
|
||||
: (
|
||||
"=" => "| %$w[0]s|%-$w[1]s |%-$w[2]s |\n",
|
||||
"A" => "* %$w[0]s|%-$w[1]s |%-$w[2]s |\n",
|
||||
"B" => "| %$w[0]s|%-$w[1]s |%-$w[2]s *\n",
|
||||
"*" => "* %$w[0]s|%-$w[1]s |%-$w[2]s *\n",
|
||||
);
|
||||
|
||||
my @args = ('', '', '');
|
||||
push(@args, '') if $four_column_mode;
|
||||
$fmts{bar} = sprintf $fmts{"="}, @args;
|
||||
$fmts{bar} =~ s/\S/+/g;
|
||||
$fmts{bar} =~ s/ /-/g;
|
||||
|
||||
# Sometimes the sprintf has too many arguments,
|
||||
# which results in a warning on Perl 5.021+
|
||||
# I really wanted to write:
|
||||
# no warnings 'redundant';
|
||||
# but that causes a compilation error on older versions of perl
|
||||
# where the warnings pragma doesn't know about 'redundant'
|
||||
no warnings;
|
||||
|
||||
return join( "",
|
||||
map {
|
||||
sprintf( $fmts{$_->[-1]}, @$_ );
|
||||
} (
|
||||
["bar"],
|
||||
@heading_lines,
|
||||
@heading_lines ? ["bar"] : (),
|
||||
@{$self->{ELTS}},
|
||||
),
|
||||
);
|
||||
|
||||
@{$self->{ELTS}} = [];
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Text::Diff::Table - Text::Diff plugin to generate "table" format output
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Text::Diff;
|
||||
|
||||
diff \@a, $b, { STYLE => "Table" };
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a plugin output formatter for Text::Diff that generates "table" style
|
||||
diffs:
|
||||
|
||||
+--+----------------------------------+--+------------------------------+
|
||||
| |../Test-Differences-0.2/MANIFEST | |../Test-Differences/MANIFEST |
|
||||
| |Thu Dec 13 15:38:49 2001 | |Sat Dec 15 02:09:44 2001 |
|
||||
+--+----------------------------------+--+------------------------------+
|
||||
| | * 1|Changes *
|
||||
| 1|Differences.pm | 2|Differences.pm |
|
||||
| 2|MANIFEST | 3|MANIFEST |
|
||||
| | * 4|MANIFEST.SKIP *
|
||||
| 3|Makefile.PL | 5|Makefile.PL |
|
||||
| | * 6|t/00escape.t *
|
||||
| 4|t/00flatten.t | 7|t/00flatten.t |
|
||||
| 5|t/01text_vs_data.t | 8|t/01text_vs_data.t |
|
||||
| 6|t/10test.t | 9|t/10test.t |
|
||||
+--+----------------------------------+--+------------------------------+
|
||||
|
||||
This format also goes to some pains to highlight "invisible" characters on
|
||||
differing elements by selectively escaping whitespace. Each element is split
|
||||
in to three segments (leading whitespace, body, trailing whitespace). If
|
||||
whitespace differs in a segement, that segment is whitespace escaped.
|
||||
|
||||
Here is an example of the selective whitespace.
|
||||
|
||||
+--+--------------------------+--------------------------+
|
||||
| |demo_ws_A.txt |demo_ws_B.txt |
|
||||
| |Fri Dec 21 08:36:32 2001 |Fri Dec 21 08:36:50 2001 |
|
||||
+--+--------------------------+--------------------------+
|
||||
| 1|identical |identical |
|
||||
* 2| spaced in | also spaced in *
|
||||
* 3|embedded space |embedded tab *
|
||||
| 4|identical |identical |
|
||||
* 5| spaced in |\ttabbed in *
|
||||
* 6|trailing spaces\s\s\n |trailing tabs\t\t\n *
|
||||
| 7|identical |identical |
|
||||
* 8|lf line\n |crlf line\r\n *
|
||||
* 9|embedded ws |embedded\tws *
|
||||
+--+--------------------------+--------------------------+
|
||||
|
||||
Here's why the lines do or do not have whitespace escaped:
|
||||
|
||||
=over
|
||||
|
||||
=item lines 1, 4, 7 don't differ, no need.
|
||||
|
||||
=item lines 2, 3 differ in non-whitespace, no need.
|
||||
|
||||
=item lines 5, 6, 8, 9 all have subtle ws changes.
|
||||
|
||||
=back
|
||||
|
||||
Whether or not line 3 should have that tab character escaped is a judgement
|
||||
call; so far I'm choosing not to.
|
||||
|
||||
=head1 UNICODE
|
||||
|
||||
To output the raw unicode chracters consult the documentation of
|
||||
L<Text::Diff::Config>. You can set the C<DIFF_OUTPUT_UNICODE> environment
|
||||
variable to 1 to output it from the command line. For more information,
|
||||
consult this bug: L<https://rt.cpan.org/Ticket/Display.html?id=54214> .
|
||||
|
||||
=head1 LIMITATIONS
|
||||
|
||||
Table formatting requires buffering the entire diff in memory in order to
|
||||
calculate column widths. This format should only be used for smaller
|
||||
diffs.
|
||||
|
||||
Assumes tab stops every 8 characters, as $DIETY intended.
|
||||
|
||||
Assumes all character codes >= 127 need to be escaped as hex codes, ie that the
|
||||
user's terminal is ASCII, and not even "high bit ASCII", capable. This can be
|
||||
made an option when the need arises.
|
||||
|
||||
Assumes that control codes (character codes 0..31) that don't have slash-letter
|
||||
escapes ("\n", "\r", etc) in Perl are best presented as hex escapes ("\x01")
|
||||
instead of octal ("\001") or control-code ("\cA") escapes.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Barrie Slaymaker E<lt>barries@slaysys.comE<gt>
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
Copyright 2001 Barrie Slaymaker, All Rights Reserved.
|
||||
|
||||
You may use this software under the terms of the GNU public license, any
|
||||
version, or the Artistic license.
|
||||
|
||||
=cut
|
||||
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
# The Linux tgz archive contains compiled nmon binaries for various Linux distribution on various type of processor: x86 / Power / arm / s390x
|
||||
|
||||
The source code of Nmon Linux is available in the Nmon Linux site: http://nmon.sourceforge.net
|
||||
@ -0,0 +1,221 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Program name: metricator_cleaner.pl
|
||||
# Compatibility: Perl x
|
||||
# Purpose - Clean nmon and csv files when retention expired
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
$version = "2.0.0";
|
||||
|
||||
use Time::Local;
|
||||
use Time::HiRes;
|
||||
use Getopt::Long;
|
||||
use POSIX 'strftime';
|
||||
use File::stat; # use the object-oriented interface to stat
|
||||
|
||||
# LOGGING INFORMATION:
|
||||
# - The program uses the standard logging Python module to display important messages in Splunk logs
|
||||
# - Every message of the script will be indexed and accessible within Splunk splunkd logs
|
||||
|
||||
#################################################
|
||||
## Arguments Parser
|
||||
#################################################
|
||||
|
||||
# Default values
|
||||
my $CSV_REPOSITORY = "csv_repository";
|
||||
my $APP = "";
|
||||
my $CONFIG_REPOSITORY = "config_repository";
|
||||
my $MAXSECONDS = "";
|
||||
my $verbose;
|
||||
|
||||
$result = GetOptions(
|
||||
"csv_repository=s" => \$CSV_REPOSITORY, # string
|
||||
"config_repository=s" => \$CONFIG_REPOSITORY, # string
|
||||
"cleancsv" => \$CLEANCSV, # string
|
||||
"approot=s" => \$APP, # string
|
||||
"maxseconds_csv=s" => \$MAXSECONDS_CSV, # string
|
||||
"version" => \$VERSION, # flag
|
||||
"help" => \$help # flag
|
||||
);
|
||||
|
||||
# Show version
|
||||
if ($VERSION) {
|
||||
print("nmon_clean.pl version $version \n");
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Show help
|
||||
if ($help) {
|
||||
|
||||
print( "
|
||||
|
||||
Help for metricator_cleaner.pl:
|
||||
|
||||
In default configuration (eg. no options specified) the script will purge any nmon file (*.nmon) in default nmon_repository
|
||||
|
||||
Available options are:
|
||||
|
||||
--cleancsv :Activate the purge of csv files from csv repository and config repository (see also options above)
|
||||
--maxseconds_csv <value> :Set the maximum file retention in seconds for csv data, every files older than this value will be permanently removed
|
||||
--approot <value> :Set a custom value for the Application root directory (default are: nmon / TA-metricator-hec-for-nmon / PA-nmon)
|
||||
--csv_repository <value> :Set a custom location for directory containing csv data (default: csv_repository)
|
||||
--config_repository <value> :Set a custom location for directory containing config data (default: config_repository)
|
||||
--version :Show current program version \n
|
||||
"
|
||||
);
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
#################################################
|
||||
## Parameters
|
||||
#################################################
|
||||
|
||||
# Default values for CSV retention (4 hours less 1 minute)
|
||||
my $MAXSECONDS_CSV_DEFAULT = 86400;
|
||||
|
||||
#################################################
|
||||
## Functions
|
||||
#################################################
|
||||
|
||||
#################################################
|
||||
## Program
|
||||
#################################################
|
||||
|
||||
# Processing starting time
|
||||
my $t_start = [Time::HiRes::gettimeofday];
|
||||
|
||||
# Local time
|
||||
my $time = strftime "%d-%m-%Y %H:%M:%S", localtime;
|
||||
|
||||
# Default Environment Variable SPLUNK_HOME, this shall be automatically defined if as the script shall be launched by Splunk
|
||||
my $SPLUNK_HOME = $ENV{SPLUNK_HOME};
|
||||
|
||||
# Verify SPLUNK_HOME definition
|
||||
if ( not $SPLUNK_HOME ) {
|
||||
print(
|
||||
"\n$time ERROR: The environment variable SPLUNK_HOME could not be verified, if you want to run this script manually you need to export it before processing \n"
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
# Discover TA-metricator-hec-for-nmon path
|
||||
if ( length($APP) == 0 ) {
|
||||
|
||||
if ( -d "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon" ) {
|
||||
$APP = "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon";
|
||||
}
|
||||
elsif ( -d "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon" ) {
|
||||
$APP = "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if ( !-d "$APP" ) {
|
||||
print(
|
||||
"\n$time ERROR: The Application root directory could be verified using your custom setting: $APP \n"
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Verify existence of APP
|
||||
if ( !-d "$APP" ) {
|
||||
print(
|
||||
"\n$time ERROR: The Application root directory could not be found, is the TA-metricator-hec-for-nmon installed ?\n"
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
# var directories
|
||||
my $APP_MAINVAR = "$SPLUNK_HOME/var/log/metricator";
|
||||
my $APP_VAR = "$APP_MAINVAR/var";
|
||||
|
||||
if ( !-d "$APP_MAINVAR" ) {
|
||||
print(
|
||||
"\n$time INFO: main var directory not found ($APP_MAINVAR), no need to run.\n"
|
||||
);
|
||||
exit 0;
|
||||
}
|
||||
|
||||
|
||||
####################################################################
|
||||
############# Main Program
|
||||
####################################################################
|
||||
|
||||
# check retention
|
||||
if ( not "$MAXSECONDS_CSV" ) {
|
||||
$MAXSECONDS_CSV = $MAXSECONDS_CSV_DEFAULT;
|
||||
}
|
||||
|
||||
# Print starting message
|
||||
print("$time Starting nmon cleaning:\n");
|
||||
print("Splunk Root Directory $SPLUNK_HOME nmon_cleaner version: $version Perl version: $] \n");
|
||||
|
||||
# Set current epoch time
|
||||
$epoc = time();
|
||||
|
||||
# If the csv switch is on, purge csv data
|
||||
|
||||
if ($CLEANCSV) {
|
||||
|
||||
# Counter
|
||||
$count = 0;
|
||||
|
||||
# CSV Items to clean
|
||||
@cleaning =
|
||||
( "$APP_VAR/$CSV_REPOSITORY/*.csv", "$APP_VAR/$CONFIG_REPOSITORY/*.csv" );
|
||||
|
||||
# Enter loop
|
||||
foreach $key (@cleaning) {
|
||||
|
||||
@files = glob($key);
|
||||
|
||||
foreach $file (@files) {
|
||||
if ( -f $file ) {
|
||||
|
||||
# Get file timestamp
|
||||
my $file_timestamp = stat($file)->mtime;
|
||||
|
||||
# Get difference
|
||||
my $timediff = $epoc - $file_timestamp;
|
||||
|
||||
# If retention has expired
|
||||
if ( $timediff > $MAXSECONDS_CSV ) {
|
||||
|
||||
# information
|
||||
print ("Max set retention of $MAXSECONDS_CSV seconds seconds expired for file: $file \n");
|
||||
|
||||
# purge file
|
||||
unlink $file;
|
||||
|
||||
# Increment counter
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $count eq 0 ) {
|
||||
print ("$key, no action required. \n");
|
||||
}
|
||||
else {
|
||||
print("INFO: $count files were permanently removed from $key \n");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#############################################
|
||||
############# Main Program End ############
|
||||
#############################################
|
||||
|
||||
# Show elapsed time
|
||||
my $t_end = [Time::HiRes::gettimeofday];
|
||||
print "Elapsed time was: ",
|
||||
Time::HiRes::tv_interval( $t_start, $t_end ) . " seconds \n";
|
||||
|
||||
exit(0);
|
||||
@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Program name: metricator_cleaner.py
|
||||
# Compatibility: Python 2.7
|
||||
# Purpose - Clean csv files when retention expires, tuned for the Coke Company
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
# Load libs
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import os
|
||||
import glob
|
||||
import time
|
||||
import logging
|
||||
import platform
|
||||
import re
|
||||
import argparse
|
||||
|
||||
# Converter version
|
||||
version = '2.0.0'
|
||||
|
||||
# LOGGING INFORMATION:
|
||||
# - The program uses the standard logging Python module to display important messages in Splunk logs
|
||||
# - Every message of the script will be indexed and accessible within Splunk splunkd logs
|
||||
|
||||
#################################################
|
||||
# Functions
|
||||
#################################################
|
||||
|
||||
# Disallow negative value in parser
|
||||
|
||||
def check_negative(value):
|
||||
|
||||
ivalue = int(value)
|
||||
if ivalue < 0:
|
||||
raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)
|
||||
return ivalue
|
||||
|
||||
#################################################
|
||||
# Arguments Parser
|
||||
#################################################
|
||||
|
||||
# Define Arguments
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('--cleancsv', action='store_true', default=False, dest='cleancsv',
|
||||
help='Activate the purge of csv files from csv repository and config repository '
|
||||
'(see also options above)')
|
||||
|
||||
parser.add_argument('--maxseconds_csv', action='store', dest='MAXSECONDS_CSV', type=check_negative,
|
||||
help='Set the maximum file retention in seconds for csv data, every files older'
|
||||
' than this value will be permanently removed')
|
||||
|
||||
parser.add_argument('--approot', action='store', dest='APP',
|
||||
help='Set a custom value for the Application root directory '
|
||||
'(default are: nmon / TA-metricator-hec-for-nmon / PA-nmon)')
|
||||
|
||||
parser.add_argument('--csv_repository', action='store', dest='CSV_REPOSITORY',
|
||||
help='Set a custom location for directory containing csv data (default: csv_repository)')
|
||||
|
||||
parser.add_argument('--config_repository', action='store', dest='CONFIG_REPOSITORY',
|
||||
help='Set a custom location for directory containing config data (default: config_repository)')
|
||||
|
||||
parser.add_argument('--version', action='version', version='%(prog)s ' + version)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
#################################################
|
||||
# Variables
|
||||
#################################################
|
||||
|
||||
# Set logging format
|
||||
logging.root
|
||||
logging.root.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(levelname)s %(message)s')
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
logging.root.addHandler(handler)
|
||||
|
||||
# Current date
|
||||
now = time.strftime("%d-%m-%Y %H:%M:%S")
|
||||
|
||||
# Set maxseconds
|
||||
maxseconds_csv = args.MAXSECONDS_CSV
|
||||
|
||||
# Set cleancsv
|
||||
cleancsv = args.cleancsv
|
||||
|
||||
# If the root directory App is no defined, use empty value (will be set later)
|
||||
if not args.APP:
|
||||
APP = ''
|
||||
else:
|
||||
APP = args.APP
|
||||
|
||||
# If the csv_repository is not defined, apply default 'csv_repository' value
|
||||
if not args.CSV_REPOSITORY:
|
||||
csv_repository = "csv_repository"
|
||||
else:
|
||||
csv_repository = args.CSV_REPOSITORY
|
||||
|
||||
# If the config_repository is not defined, apply default 'config_repository' value
|
||||
if not args.CONFIG_REPOSITORY:
|
||||
config_repository = "config_repository"
|
||||
else:
|
||||
config_repository = args.CONFIG_REPOSITORY
|
||||
|
||||
# Guest Operation System type
|
||||
ostype = platform.system().lower()
|
||||
|
||||
# If running Windows OS (used for directory identification)
|
||||
is_windows = re.match(r'^win\w+', (platform.system().lower()))
|
||||
|
||||
# Python version
|
||||
python_version = platform.python_version()
|
||||
|
||||
# Verify SPLUNK_HOME environment variable is available, the script is expected to be launched by Splunk which
|
||||
# will set this for debugging or manual run, please set this variable manually
|
||||
try:
|
||||
os.environ["SPLUNK_HOME"]
|
||||
except KeyError:
|
||||
logging.error('The environment variable SPLUNK_HOME could not be verified, if you want to run this script '
|
||||
'manually you need to export it before processing')
|
||||
sys.exit(1)
|
||||
|
||||
# SPLUNK_HOME environment variable
|
||||
SPLUNK_HOME = os.environ['SPLUNK_HOME']
|
||||
|
||||
# Set APP root directory
|
||||
if not APP:
|
||||
|
||||
# Discover TA-metricator-hec-for-nmon path
|
||||
|
||||
if is_windows:
|
||||
TA_NMON_APP = SPLUNK_HOME + '\\etc\\apps\\TA-metricator-hec-for-nmon'
|
||||
else:
|
||||
TA_NMON_APP = SPLUNK_HOME + '/etc/apps/TA-metricator-hec-for-nmon'
|
||||
|
||||
if is_windows:
|
||||
TA_NMON_APP_CLUSTERED = SPLUNK_HOME + '\\etc\\slave-apps\\TA-metricator-hec-for-nmon'
|
||||
else:
|
||||
TA_NMON_APP_CLUSTERED = SPLUNK_HOME + '/etc/slave-apps/TA-metricator-hec-for-nmon'
|
||||
|
||||
# Verify APP exist
|
||||
if os.path.exists(TA_NMON_APP):
|
||||
APP = TA_NMON_APP
|
||||
|
||||
elif os.path.exists(TA_NMON_APP_CLUSTERED):
|
||||
APP = TA_NMON_APP_CLUSTERED
|
||||
|
||||
else:
|
||||
msg = 'The Application root directory could not be found, is the TA-metricator-hec-for-nmon ? We tried: '\
|
||||
+ str(TA_NMON_APP) + ' ' + str(TA_NMON_APP_CLUSTERED)
|
||||
logging.error(msg)
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
|
||||
if is_windows:
|
||||
NMON_APP = SPLUNK_HOME + '\\etc\\apps\\' + APP
|
||||
else:
|
||||
NMON_APP = SPLUNK_HOME + '/etc/apps/' + APP
|
||||
|
||||
# Verify APP exist
|
||||
if os.path.exists(NMON_APP):
|
||||
APP = NMON_APP
|
||||
else:
|
||||
msg = 'The Application root directory could not be found, is the TA-metricator-hec-for-nmon installed ? We tried: '\
|
||||
+ str(NMON_APP)
|
||||
logging.error(msg)
|
||||
sys.exit(1)
|
||||
|
||||
# APP_MAINVAR and APP_VAR directories
|
||||
if is_windows:
|
||||
APP_MAINVAR = SPLUNK_HOME + '\\var\\log\\nmon'
|
||||
APP_VAR = APP_MAINVAR + '\\var'
|
||||
else:
|
||||
APP_MAINVAR = SPLUNK_HOME + '/var/log/metricator'
|
||||
APP_VAR = APP_MAINVAR + '/var'
|
||||
|
||||
|
||||
if not os.path.exists(APP_MAINVAR):
|
||||
msg = 'The main var directory ' + APP_VAR + ' has not been found, there is no need to run now.'
|
||||
sys.exit(1)
|
||||
|
||||
# Repositories definition
|
||||
if is_windows:
|
||||
CSV_DIR = APP_VAR + '\\' + csv_repository
|
||||
CONFIG_DIR = APP_VAR + '\\' + config_repository
|
||||
else:
|
||||
CSV_DIR = APP_VAR + '/' + csv_repository
|
||||
CONFIG_DIR = APP_VAR + '/' + config_repository
|
||||
|
||||
# List of directories to be proceeded
|
||||
WORKING_DIR = {CSV_DIR, CONFIG_DIR}
|
||||
|
||||
# Starting time of process
|
||||
start_time = time.time()
|
||||
|
||||
####################################################################
|
||||
# Main Program
|
||||
####################################################################
|
||||
|
||||
# Default value for CSV retention
|
||||
if maxseconds_csv is None:
|
||||
maxseconds_csv = 86400
|
||||
|
||||
# Show current time
|
||||
msg = now + " Starting nmon cleaning"
|
||||
print (msg)
|
||||
|
||||
# Display some basic information about us
|
||||
msg = "Splunk Root Directory ($SPLUNK_HOME): " + str(SPLUNK_HOME) + " nmon_cleaner version: " + str(version) \
|
||||
+ " Python version: " + str(python_version)
|
||||
print (msg)
|
||||
|
||||
# Proceed to CSV cleaning
|
||||
if cleancsv:
|
||||
|
||||
for DIR in WORKING_DIR:
|
||||
|
||||
if os.path.exists(DIR):
|
||||
# cd to directory
|
||||
os.chdir(DIR)
|
||||
|
||||
# Verify we have data to manage
|
||||
counter = len(glob.glob1(DIR, "*.csv"))
|
||||
|
||||
# print (counter)
|
||||
|
||||
if counter == 0:
|
||||
msg = str(DIR) + ', no action required.'
|
||||
print (msg)
|
||||
|
||||
else:
|
||||
|
||||
# counter of files with retention expired
|
||||
counter_expired = 0
|
||||
|
||||
curtime = time.time()
|
||||
limit = maxseconds_csv
|
||||
|
||||
for xfile in glob.glob('*.csv'):
|
||||
|
||||
filemtime = os.path.getmtime(xfile)
|
||||
|
||||
if curtime - filemtime > limit:
|
||||
|
||||
counter_expired += 1
|
||||
|
||||
size_mb = os.path.getsize(xfile)/1000.0/1000.0
|
||||
size_mb = format(size_mb, '.2f')
|
||||
|
||||
mtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(filemtime)) # Human readable datetime
|
||||
|
||||
msg = 'Max set retention of ' + str(maxseconds_csv) + ' seconds expired for file: ' +\
|
||||
xfile + ' size(MB): '\
|
||||
+ str(size_mb) + ' mtime: ' + str(mtime)
|
||||
print (msg)
|
||||
|
||||
os.remove(xfile) # Permanently remove the file!
|
||||
|
||||
if counter_expired != 0:
|
||||
msg = str(counter_expired) + ' files were permanently removed due to retention expired' \
|
||||
' for directory ' + DIR
|
||||
else:
|
||||
msg = str(DIR) + ', no action required.'
|
||||
print (msg)
|
||||
|
||||
###################
|
||||
# End
|
||||
###################
|
||||
|
||||
# Time required to process
|
||||
end_time = time.time()
|
||||
result = "Elapsed time was: %g seconds" % (end_time - start_time)
|
||||
print (result)
|
||||
|
||||
# exit
|
||||
sys.exit(0)
|
||||
@ -0,0 +1,368 @@
|
||||
#!/bin/sh
|
||||
|
||||
# set -x
|
||||
|
||||
# Program name: metricator_cleaner.sh
|
||||
# Purpose - Frontal script to metricator_cleaner.py and metricator_cleaner.pl, will launch Python or Perl script depending on interpreter availability
|
||||
# See metricator_cleaner.py | metricator_cleaner.pl
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
# Version 2.0.1
|
||||
|
||||
# For AIX / Linux / Solaris
|
||||
|
||||
#################################################
|
||||
## Your Customizations Go Here ##
|
||||
#################################################
|
||||
|
||||
# format date output to strftime dd/mm/YYYY HH:MM:SS
|
||||
log_date () {
|
||||
date "+%d-%m-%Y %H:%M:%S"
|
||||
}
|
||||
|
||||
# hostname
|
||||
HOST=`hostname`
|
||||
|
||||
# Which type of OS are we running
|
||||
UNAME=`uname`
|
||||
|
||||
if [ -z "${SPLUNK_HOME}" ]; then
|
||||
echo "`log_date`, ${HOST} ERROR, SPLUNK_HOME variable is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# APP path discovery
|
||||
if [ -d "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon" ]; then
|
||||
APP=$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon
|
||||
|
||||
elif [ -d "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon" ];then
|
||||
APP=$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon
|
||||
|
||||
else
|
||||
echo "`log_date`, ${HOST} ERROR, the APP directory could not be defined, is the TA-metricator-hec-for-nmon installed ?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# source default nmon.conf
|
||||
if [ -f $APP/default/nmon.conf ]; then
|
||||
# During initial deployment, the nmon.conf needs to be managed properly by the metricator_consumer.sh
|
||||
# wait for this to be done
|
||||
grep '\[nmon\]' $APP/default/nmon.conf >/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "`log_date`, ${HOST} INFO, initial deployment condition detected, safe exiting."
|
||||
exit 0
|
||||
else
|
||||
. $APP/default/nmon.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
# source local nmon.conf, if any
|
||||
|
||||
# Search for a local nmon.conf file located in $SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local
|
||||
if [ -f $APP/local/nmon.conf ]; then
|
||||
. $APP/local/nmon.conf
|
||||
fi
|
||||
|
||||
# On a per server basis, you can also set in /etc/nmon.conf
|
||||
if [ -f /etc/nmon.conf ]; then
|
||||
. /etc/nmon.conf
|
||||
fi
|
||||
|
||||
# Manage FQDN option
|
||||
echo $nmonparser_options | grep '\-\-use_fqdn' >/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
# Only relevant for Linux OS
|
||||
case $UNAME in
|
||||
Linux)
|
||||
HOST=`hostname -f` ;;
|
||||
AIX)
|
||||
HOST=`hostname` ;;
|
||||
SunOS)
|
||||
HOST=`hostname` ;;
|
||||
esac
|
||||
else
|
||||
HOST=`hostname`
|
||||
fi
|
||||
|
||||
# Manage host override option based on Splunk hostname defined
|
||||
case $override_sys_hostname in
|
||||
"1")
|
||||
# Retrieve the Splunk host value
|
||||
HOST=`cat $SPLUNK_HOME/etc/system/local/inputs.conf | grep '^host =' | awk -F\= '{print $2}' | sed 's/ //g'`
|
||||
;;
|
||||
esac
|
||||
|
||||
#
|
||||
# Interpreter choice
|
||||
#
|
||||
|
||||
PYTHON=0
|
||||
PYTHON2=0
|
||||
PYTHON3=0
|
||||
PERL=0
|
||||
# Set the default interpreter
|
||||
INTERPRETER="python"
|
||||
|
||||
# Get the version for both worlds
|
||||
PYTHON2=`which python 2>&1`
|
||||
PYTHON3=`which python3 2>&1`
|
||||
PERL=`which perl 2>&1`
|
||||
|
||||
# Handle Python
|
||||
PYTHON_available="false"
|
||||
case $PYTHON3 in
|
||||
*python*)
|
||||
PYTHON_available="true"
|
||||
INTERPRETER="python3" ;;
|
||||
*)
|
||||
case $PYTHON2 in
|
||||
*python*)
|
||||
PYTHON_available="true"
|
||||
INTERPRETER="python" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# Handle Perl
|
||||
case $PERL in
|
||||
*perl*)
|
||||
PERL_available="true"
|
||||
;;
|
||||
*)
|
||||
PERL_available="false"
|
||||
;;
|
||||
esac
|
||||
|
||||
case `uname` in
|
||||
|
||||
# AIX priority is Perl
|
||||
"AIX")
|
||||
case $PERL_available in
|
||||
"true")
|
||||
INTERPRETER="perl" ;;
|
||||
"false")
|
||||
INTERPRETER="$INTERPRETER" ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
# Other OS, priority is Python
|
||||
*)
|
||||
case $PYTHON_available in
|
||||
"true")
|
||||
INTERPRETER="$INTERPRETER" ;;
|
||||
"false")
|
||||
INTERPRETER="perl" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# POSIX process run time in seconds (for Solaris only)
|
||||
P_RUNTIME () {
|
||||
t=`LC_ALL=POSIX ps -o etime= -p $1 | awk '{print $1}'`
|
||||
d=0 h=0
|
||||
case $t in *-*) d=$((0 + ${t%%-*})); t=${t#*-};; esac
|
||||
case $t in *:*:*) h=$((0 + ${t%%:*})); t=${t#*:};; esac
|
||||
s=$((10#$d*86400 + 10#$h*3600 + 10#${t%%:*}*60 + 10#${t#*:}))
|
||||
echo $s
|
||||
}
|
||||
|
||||
####################################################################
|
||||
############# Main Program ############
|
||||
####################################################################
|
||||
|
||||
# Store arguments sent to script
|
||||
userargs=$@
|
||||
|
||||
###### Maintenance tasks ######
|
||||
|
||||
#
|
||||
# Maintenance task1
|
||||
#
|
||||
|
||||
# Maintenance task 1: verify if we have nmon processes running over the allowed period
|
||||
# This issue seems to happen sometimes specially on AIX servers
|
||||
|
||||
# If an nmon process has not been terminated after its grace period, the process will be killed
|
||||
|
||||
# get the allowed runtime in seconds for an nmon process according to the configuration
|
||||
# and add a 10 minute grace period
|
||||
|
||||
case `uname` in
|
||||
|
||||
"AIX"|"Linux"|"SunOS")
|
||||
|
||||
echo "`log_date`, ${HOST} INFO, starting maintenance task 1: verify nmon processes running over expected time period"
|
||||
|
||||
endtime=0
|
||||
|
||||
case ${mode_fifo} in
|
||||
"1")
|
||||
endtime=`expr ${fifo_interval} \* ${fifo_snapshot}` ;;
|
||||
*)
|
||||
endtime=`expr ${interval} \* ${snapshot}` ;;
|
||||
esac
|
||||
|
||||
endtime=`expr ${endtime} + 600`
|
||||
|
||||
# get the list of running processes
|
||||
case $UNAME in
|
||||
"AIX"|"Linux")
|
||||
oldPidList=`ps -eo user,pid,command,etime,args | grep "nmon" | grep "splunk" | grep "var/log/metricator" | grep -v metricator_reader | grep -v grep | awk '{ print $2 }'`
|
||||
ps -eo user,pid,command,etime,args | grep "nmon" | grep "splunk" | grep "var/log/metricator" | grep -v metricator_reader | grep -v grep >/dev/null ;;
|
||||
"SunOS")
|
||||
oldPidList=`ps auxwww | grep "sadc" | grep "splunk" | grep "var/log/metricator" | grep -v metricator_reader | grep -v grep | awk '{ print $2 }'`
|
||||
ps auxwww | grep "sadc" | grep "splunk" | grep "var/log/metricator" | grep -v metricator_reader | grep -v grep >/dev/null ;;
|
||||
esac
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
|
||||
for pid in $oldPidList; do
|
||||
|
||||
pid_runtime=0
|
||||
# only run the process is running
|
||||
if [ -d /proc/${pid} ]; then
|
||||
# get the process runtime in seconds
|
||||
|
||||
case $UNAME in
|
||||
"AIX"|"Linux")
|
||||
pid_runtime=`ps -p ${pid} -oetime= | tr '-' ':' | awk -F: '{ total=0; m=1; } { for (i=0; i < NF; i++) {total += $(NF-i)*m; m *= i >= 2 ? 24 : 60 }} {print total}'`
|
||||
;;
|
||||
"SunOS")
|
||||
pid_runtime=`P_RUNTIME ${pid}`
|
||||
;;
|
||||
esac
|
||||
|
||||
# additional protection
|
||||
case ${pid_runtime} in
|
||||
"")
|
||||
;;
|
||||
*)
|
||||
if [ ${pid_runtime} -gt ${endtime} ]; then
|
||||
echo "`log_date`, ${HOST} WARN, old nmon process found due to: `ps auxwww | grep $pid | grep -v grep` killing (SIGTERM) process $pid"
|
||||
kill $pid
|
||||
|
||||
# Allow some time for the process to end
|
||||
sleep 5
|
||||
|
||||
# re-check the status
|
||||
ps -p ${pid} -oetime= >/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "`log_date`, ${HOST} WARN, old nmon process found due to: `ps auxwww | grep $pid | grep -v grep` failed to stop, killing (-9) process $pid"
|
||||
kill -9 $pid
|
||||
fi
|
||||
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
#
|
||||
# Maintenance task2
|
||||
# set -x
|
||||
# - manage any fifo reader orphan processes (no associated nmon process)
|
||||
# - manage any fifo reader duplicated (abnormal situation)
|
||||
|
||||
echo "`log_date`, ${HOST} INFO, starting maintenance task 2: verify orphan or duplicated fifo_reader processes"
|
||||
|
||||
for instance in fifo1 fifo2; do
|
||||
|
||||
# Initiate
|
||||
oldPidNb=0
|
||||
|
||||
case $INTERPRETER in
|
||||
"perl")
|
||||
readerNbProc=2 ;;
|
||||
"python"|"python3")
|
||||
readerNbProc=3 ;;
|
||||
esac
|
||||
|
||||
# get the list of running processes
|
||||
ps auxwww | grep "nmon" | grep "splunk" | grep metricator_reader | grep ${instance} >/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
|
||||
oldPidList=`ps auxwwww | grep "nmon" | grep "splunk" | grep metricator_reader | grep ${instance} | grep -v grep | awk '{ print $2 }'`
|
||||
oldPidNb=`ps auxwww | grep "nmon" | grep "splunk" | grep metricator_reader | grep ${instance} | grep -v grep | wc -l | awk '{print $1}'`
|
||||
|
||||
# search for associated nmon process
|
||||
case $UNAME in
|
||||
"AIX"|"Linux")
|
||||
ps auxwww | grep "nmon" | grep "splunk" | grep "var/log/metricator" | grep -v metricator_reader | grep ${instance} >/dev/null
|
||||
;;
|
||||
"SunOS")
|
||||
ps auxwww | grep "sadc" | grep "splunk" | grep "var/log/metricator" | grep -v metricator_reader | grep ${instance} >/dev/null
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $? -ne 0 ] && [ $oldPidNb -eq $readerNbProc ]; then
|
||||
|
||||
# no process found, kill the reader processes
|
||||
for pid in $oldPidList; do
|
||||
echo "`log_date`, ${HOST} WARN, orphan reader process found (no associated nmon process) due to: `ps auxwww | grep $pid | grep -v grep` killing (SIGTERM) process $pid"
|
||||
kill $pid
|
||||
|
||||
# Allow some time for the process to end
|
||||
sleep 5
|
||||
|
||||
# re-check the status
|
||||
ps -p ${pid} -oetime= >/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "`log_date`, ${HOST} WARN, orphan reader process (no associated nmon process) due to: `ps auxwww | grep $pid | grep -v grep` failed to stop, killing (-9) process $pid"
|
||||
kill -9 $pid
|
||||
fi
|
||||
done
|
||||
|
||||
# If nmon is running but the number of reader processes is higher than 2 (shell parent + Python/Perl child), something went wrong
|
||||
elif [ $oldPidNb -gt $readerNbProc ]; then
|
||||
|
||||
echo "`log_date`, ${HOST} WARN, multiple reader for the same fifo were detected, this is an abnormal situation and reader will be killed."
|
||||
|
||||
# no process found, kill the reader processes
|
||||
for pid in $oldPidList; do
|
||||
echo "`log_date`, ${HOST} WARN, duplicated reader process found due to: `ps auxwww | grep $pid | grep -v grep` killing (SIGTERM) process $pid"
|
||||
kill $pid
|
||||
|
||||
# Allow some time for the process to end
|
||||
sleep 5
|
||||
|
||||
# re-check the status
|
||||
ps -p ${pid} -oetime= >/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "`log_date`, ${HOST} WARN, duplicated reader process found due to: `ps auxwww | grep $pid | grep -v grep` failed to stop, killing (-9) process $pid"
|
||||
kill -9 $pid
|
||||
fi
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
;;
|
||||
|
||||
# End of per OS case
|
||||
esac
|
||||
|
||||
###### End maintenance tasks ######
|
||||
|
||||
###### Start cleaner ######
|
||||
|
||||
case ${INTERPRETER} in
|
||||
|
||||
"python"|"python3")
|
||||
$INTERPRETER $APP/bin/metricator_cleaner.py ${userargs} ;;
|
||||
|
||||
"perl")
|
||||
$APP/bin/metricator_cleaner.pl ${userargs} ;;
|
||||
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@ -0,0 +1,367 @@
|
||||
#!/bin/sh
|
||||
|
||||
# set -x
|
||||
|
||||
# Program name: metricator_consumer.sh
|
||||
# Purpose - consume data produced by the fifo readers
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
# Version 2.0.0
|
||||
|
||||
# For AIX / Linux / Solaris
|
||||
|
||||
#################################################
|
||||
## Your Customizations Go Here ##
|
||||
#################################################
|
||||
|
||||
# hostname
|
||||
HOST=`hostname`
|
||||
|
||||
# Which type of OS are we running
|
||||
UNAME=`uname`
|
||||
|
||||
# format date output to strftime dd/mm/YYYY HH:MM:SS
|
||||
log_date () {
|
||||
date "+%d-%m-%Y %H:%M:%S"
|
||||
}
|
||||
|
||||
if [ -z "${SPLUNK_HOME}" ]; then
|
||||
echo "`log_date`, ${HOST} ERROR, SPLUNK_HOME variable is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# check and wait to acquire mutex
|
||||
mutex="$SPLUNK_HOME/var/log/metricator/mutex"
|
||||
|
||||
remove_mutex () {
|
||||
rm -f $mutex
|
||||
}
|
||||
|
||||
# Allow 10s mini to acquire mutex and break
|
||||
count=0
|
||||
while [ -f $mutex ]; do
|
||||
sleep 2
|
||||
count=`expr $count + 1`
|
||||
if [ $count -gt 5 ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# acquire mutex
|
||||
if [ -d $SPLUNK_HOME/var/log/metricator ]; then
|
||||
touch $mutex
|
||||
fi
|
||||
|
||||
# tmp dir and file
|
||||
temp_dir="${SPLUNK_HOME}/var/log/metricator/tmp/"
|
||||
|
||||
if [ ! -d ${temp_dir} ]; then
|
||||
mkdir -p ${temp_dir}
|
||||
fi
|
||||
|
||||
temp_file="${temp_dir}/metricator_consumer.sh.$$"
|
||||
|
||||
# Splunk Home variable: This should automatically defined when this script is being launched by Splunk
|
||||
# If you intend to run this script out of Splunk, please set your custom value here
|
||||
SPL_HOME=${SPLUNK_HOME}
|
||||
|
||||
# Check SPL_HOME variable is defined, this should be the case when launched by Splunk scheduler
|
||||
if [ -z "${SPL_HOME}" ]; then
|
||||
echo "`log_date`, ${HOST} ERROR, SPL_HOME (SPLUNK_HOME) variable is not defined"
|
||||
remove_mutex
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# APP path discovery
|
||||
if [ -d "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon" ]; then
|
||||
APP=$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon
|
||||
|
||||
elif [ -d "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon" ];then
|
||||
APP=$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon
|
||||
|
||||
else
|
||||
echo "`log_date`, ${HOST} ERROR, the APP directory could not be defined, is the TA-metricator-hec-for-nmon installed ?"
|
||||
remove_mutex
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# Interpreter choice
|
||||
#
|
||||
|
||||
PYTHON=0
|
||||
PYTHON2=0
|
||||
PYTHON3=0
|
||||
PERL=0
|
||||
# Set the default interpreter
|
||||
INTERPRETER="python"
|
||||
|
||||
# Get the version for both worlds
|
||||
PYTHON2=`which python 2>&1`
|
||||
PYTHON3=`which python3 2>&1`
|
||||
PERL=`which perl 2>&1`
|
||||
|
||||
# Handle Python
|
||||
PYTHON_available="false"
|
||||
case $PYTHON3 in
|
||||
*python*)
|
||||
PYTHON_available="true"
|
||||
INTERPRETER="python3" ;;
|
||||
*)
|
||||
case $PYTHON2 in
|
||||
*python*)
|
||||
PYTHON_available="true"
|
||||
INTERPRETER="python" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# Handle Perl
|
||||
case $PERL in
|
||||
*perl*)
|
||||
PERL_available="true"
|
||||
;;
|
||||
*)
|
||||
PERL_available="false"
|
||||
;;
|
||||
esac
|
||||
|
||||
case `uname` in
|
||||
|
||||
# AIX priority is Perl
|
||||
"AIX")
|
||||
case $PERL_available in
|
||||
"true")
|
||||
INTERPRETER="perl" ;;
|
||||
"false")
|
||||
INTERPRETER="$INTERPRETER" ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
# Other OS, priority is Python
|
||||
*)
|
||||
case $PYTHON_available in
|
||||
"true")
|
||||
INTERPRETER="$INTERPRETER" ;;
|
||||
"false")
|
||||
INTERPRETER="perl" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# default values relevant for our context
|
||||
nmonparser_options="--mode fifo"
|
||||
|
||||
# source default nmon.conf
|
||||
if [ -f $APP/default/nmon.conf ]; then
|
||||
# During initial deployment, the nmon.conf needs to be managed properly by the metricator_consumer.sh
|
||||
# wait for this to be done
|
||||
grep '\[nmon\]' $APP/default/nmon.conf >/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "`log_date`, ${HOST} INFO, initial deployment condition detected, safe exiting."
|
||||
exit 0
|
||||
else
|
||||
. $APP/default/nmon.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
# source local nmon.conf, if any
|
||||
|
||||
# Search for a local nmon.conf file located in $SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local
|
||||
if [ -f $APP/local/nmon.conf ]; then
|
||||
. $APP/local/nmon.conf
|
||||
fi
|
||||
|
||||
# On a per server basis, you can also set in /etc/nmon.conf
|
||||
if [ -f /etc/nmon.conf ]; then
|
||||
. /etc/nmon.conf
|
||||
fi
|
||||
|
||||
# Manage FQDN option
|
||||
echo $nmonparser_options | grep '\-\-use_fqdn' >/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
# Only relevant for Linux OS
|
||||
case $UNAME in
|
||||
Linux)
|
||||
HOST=`hostname -f` ;;
|
||||
AIX)
|
||||
HOST=`hostname` ;;
|
||||
SunOS)
|
||||
HOST=`hostname` ;;
|
||||
esac
|
||||
else
|
||||
HOST=`hostname`
|
||||
fi
|
||||
|
||||
# Manage host override option based on Splunk hostname defined
|
||||
case $override_sys_hostname in
|
||||
"1")
|
||||
# Retrieve the Splunk host value
|
||||
HOST=`cat $SPLUNK_HOME/etc/system/local/inputs.conf | grep '^host =' | awk -F\= '{print $2}' | sed 's/ //g'`
|
||||
;;
|
||||
esac
|
||||
|
||||
############################################
|
||||
# functions
|
||||
############################################
|
||||
|
||||
# consume function
|
||||
consume_data () {
|
||||
|
||||
# fifo name (valid choices are: fifo1 | fifo2)
|
||||
FIFO=$1
|
||||
|
||||
# consume fifo
|
||||
|
||||
# realtime
|
||||
nmon_config=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_config.dat
|
||||
nmon_header=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_header.dat
|
||||
nmon_timestamp=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_timestamp.dat
|
||||
nmon_data=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_data.dat
|
||||
nmon_data_tmp=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_data_tmp.dat
|
||||
nmon_external=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_external.dat
|
||||
nmon_external_header=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_external_header.dat
|
||||
|
||||
|
||||
# rotated
|
||||
nmon_config_rotated=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_config.dat.rotated
|
||||
nmon_header_rotated=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_header.dat.rotated
|
||||
nmon_timestamp_rotated=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_timestamp.dat.rotated
|
||||
nmon_data_rotated=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_data.dat.rotated
|
||||
nmon_external_rotated=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_external.dat.rotated
|
||||
nmon_external_header_rotated=$SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/nmon_external_header.dat.rotated
|
||||
|
||||
# manage rotated data if existing, prevent any data loss
|
||||
|
||||
# all files must be existing to be managed
|
||||
if [ -s $nmon_config_rotated ] && [ -s $nmon_header_rotated ] && [ -s $nmon_data_rotated ]; then
|
||||
|
||||
# Manager headers
|
||||
unset nmon_header_files
|
||||
if [ -f $nmon_external_header_rotated ]; then
|
||||
nmon_header_files="$nmon_header_rotated $nmon_external_header_rotated"
|
||||
else
|
||||
nmon_header_files="$nmon_header_rotated"
|
||||
fi
|
||||
|
||||
# Ensure the first line of nmon_data starts by the relevant timestamp, if not add it
|
||||
head -1 $nmon_data_rotated | grep 'ZZZZ,T' >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
# check timestamp dat exists before processing
|
||||
# there is no else possible, if the the timestamp data file does not exist, there is nothing we can do
|
||||
# and the parser will raise an error
|
||||
if [ -f $nmon_timestamp_rotated ]; then
|
||||
tail -1 $nmon_timestamp_rotated >$temp_file
|
||||
cat $nmon_config_rotated $nmon_header_files $temp_file $nmon_data_rotated $nmon_external_rotated | $SPLUNK_HOME/bin/splunk cmd $APP/bin/nmonparser.sh $nmonparser_options
|
||||
fi
|
||||
else
|
||||
cat $nmon_config_rotated $nmon_header_files $nmon_data_rotated $nmon_external_rotated | $SPLUNK_HOME/bin/splunk cmd $APP/bin/nmonparser.sh $nmonparser_options
|
||||
fi
|
||||
|
||||
# remove rotated
|
||||
rm -f $SPLUNK_HOME/var/log/metricator/var/nmon_repository/$FIFO/*.dat.rotated
|
||||
|
||||
# header var
|
||||
unset nmon_header_files
|
||||
|
||||
fi
|
||||
|
||||
# Manage realtime files
|
||||
|
||||
# all files must be existing to be managed
|
||||
if [ -s $nmon_config ] && [ -s $nmon_header ] && [ -s $nmon_data ]; then
|
||||
|
||||
# get data mtime
|
||||
case $INTERPRETER in
|
||||
"perl")
|
||||
perl -e "\$mtime=(stat(\"$nmon_data\"))[9]; \$cur_time=time(); print \$cur_time - \$mtime;" >$temp_file
|
||||
nmon_data_mtime=`cat $temp_file`
|
||||
;;
|
||||
"python"|"python3")
|
||||
$INTERPRETER -c "import os; import time; now = time.strftime(\"%s\"); print(int(int(now)-(os.path.getmtime('$nmon_data'))))" >$temp_file
|
||||
nmon_data_mtime=`cat $temp_file`
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# file should have last mtime of mini 5 sec
|
||||
|
||||
while [ $nmon_data_mtime -lt 5 ];
|
||||
do
|
||||
|
||||
sleep 1
|
||||
|
||||
# get data mtime
|
||||
case $INTERPRETER in
|
||||
"perl")
|
||||
perl -e "\$mtime=(stat(\"$nmon_data\"))[9]; \$cur_time=time(); print \$cur_time - \$mtime;" >$temp_file
|
||||
nmon_data_mtime=`cat $temp_file`
|
||||
;;
|
||||
"python"|"python3")
|
||||
$INTERPRETER -c "import os; import time; now = time.strftime(\"%s\"); print(int(int(now)-(os.path.getmtime('$nmon_data'))))" >$temp_file
|
||||
nmon_data_mtime=`cat $temp_file`
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
done
|
||||
|
||||
# copy content
|
||||
cat $nmon_data > $nmon_data_tmp
|
||||
|
||||
# nmon external data
|
||||
if [ -f $nmon_external ]; then
|
||||
cat $nmon_external >> $nmon_data_tmp
|
||||
fi
|
||||
|
||||
# empty the nmon_data file & external
|
||||
> $nmon_data
|
||||
> $nmon_external
|
||||
|
||||
# Manager headers
|
||||
unset nmon_header_files
|
||||
if [ -f $nmon_external_header ]; then
|
||||
nmon_header_files="$nmon_header $nmon_external_header"
|
||||
else
|
||||
nmon_header_files="$nmon_header"
|
||||
fi
|
||||
|
||||
# Ensure the first line of nmon_data starts by the relevant timestamp, if not add it
|
||||
head -1 $nmon_data_tmp | grep 'ZZZZ,T' >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
tail -1 $nmon_timestamp >$temp_file
|
||||
cat $nmon_config $nmon_header_files $temp_file $nmon_data_tmp | $SPLUNK_HOME/bin/splunk cmd $APP/bin/nmonparser.sh $nmonparser_options
|
||||
else
|
||||
cat $nmon_config $nmon_header_files $nmon_data_tmp | $SPLUNK_HOME/bin/splunk cmd $APP/bin/nmonparser.sh $nmonparser_options
|
||||
fi
|
||||
|
||||
# remove the copy
|
||||
rm -f $nmon_data_tmp
|
||||
|
||||
# header var
|
||||
unset nmon_header_files
|
||||
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
####################################################################
|
||||
############# Main Program ############
|
||||
####################################################################
|
||||
|
||||
# consume fifo1
|
||||
consume_data fifo1
|
||||
|
||||
# allow 1 sec idle
|
||||
sleep 1
|
||||
|
||||
# consume fifo2
|
||||
consume_data fifo2
|
||||
|
||||
# remove the temp file
|
||||
if [ -f $temp_file ]; then
|
||||
rm -f $temp_file
|
||||
fi
|
||||
|
||||
remove_mutex
|
||||
exit 0
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,248 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Program name: metricator_reader.pl
|
||||
# Compatibility: Perl x
|
||||
# Purpose - read nmon data from fifo file
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
my $version = "2.0.0";
|
||||
|
||||
use Getopt::Long;
|
||||
use File::stat;
|
||||
use File::Copy;
|
||||
use POSIX 'strftime';
|
||||
|
||||
#################################################
|
||||
## Arguments Parser
|
||||
#################################################
|
||||
|
||||
# Default values
|
||||
my $APP = "";
|
||||
my $fifo_name = "";
|
||||
my $VERSION = "";
|
||||
my $help = "";
|
||||
|
||||
my $result = GetOptions(
|
||||
"fifo=s" => \$fifo_name, # string
|
||||
"version" => \$VERSION, # flag
|
||||
"help" => \$help # flag
|
||||
);
|
||||
|
||||
# Show version
|
||||
if ($VERSION) {
|
||||
print("metricator_reader.pl version $version \n");
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Show help
|
||||
if ($help) {
|
||||
|
||||
print( "
|
||||
|
||||
Help for metricator_reader.pl:
|
||||
|
||||
The script should be run in the backgroud to continously read nmon data from fifo files.
|
||||
|
||||
Available options are:
|
||||
|
||||
--fifo <name of fifo> :Name of the pre-configured fifo file
|
||||
--version :Show current program version \n
|
||||
"
|
||||
);
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Local time
|
||||
my $time = strftime "%d-%m-%Y %H:%M:%S", localtime;
|
||||
|
||||
# Default Environment Variable SPLUNK_HOME, this shall be automatically defined if as the script shall be launched by Splunk
|
||||
my $SPLUNK_HOME = $ENV{SPLUNK_HOME};
|
||||
|
||||
# Verify SPLUNK_HOME definition
|
||||
if ( not $SPLUNK_HOME ) {
|
||||
print(
|
||||
"\n$time ERROR: The environment variable SPLUNK_HOME could not be verified, if you want to run this script manually you need to export it before processing \n"
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
# Discover TA-metricator-hec-for-nmon path
|
||||
if ( length($APP) == 0 ) {
|
||||
|
||||
if ( -d "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon" ) {
|
||||
$APP = "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon";
|
||||
}
|
||||
elsif ( -d "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon" ) {
|
||||
$APP = "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if ( !-d "$APP" ) {
|
||||
print(
|
||||
"\n$time ERROR: The Application root directory could be verified using your custom setting: $APP \n"
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Verify existence of APP
|
||||
if ( !-d "$APP" ) {
|
||||
print(
|
||||
"\n$time ERROR: The Application root directory could not be found, is the TA-metricator-hec-for-nmon installed ?\n"
|
||||
);
|
||||
die;
|
||||
}
|
||||
|
||||
# var directories
|
||||
my $APP_MAINVAR = "$SPLUNK_HOME/var/log/metricator";
|
||||
my $APP_VAR = "$APP_MAINVAR/var";
|
||||
|
||||
if ( !-d "$APP_MAINVAR" ) {
|
||||
print(
|
||||
"\n$time INFO: main var directory not found ($APP_MAINVAR), no need to run.\n"
|
||||
);
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# check fifo_name
|
||||
if ( not "$fifo_name" ) {
|
||||
print("\n$time ERROR: the --fifo_name <name of fifo> is mandatory\n");
|
||||
die;
|
||||
}
|
||||
|
||||
# define the full path to the fifo file
|
||||
my $fifo_path = "$APP_VAR/nmon_repository/$fifo_name/nmon.fifo";
|
||||
|
||||
# At startup, rotate any existing non empty .dat file if nmon_data.dat is not empty
|
||||
|
||||
# define the various files to be written
|
||||
|
||||
# realtime files
|
||||
my $nmon_config_dat = "$APP_VAR/nmon_repository/$fifo_name/nmon_config.dat";
|
||||
my $nmon_header_dat = "$APP_VAR/nmon_repository/$fifo_name/nmon_header.dat";
|
||||
my $nmon_data_dat = "$APP_VAR/nmon_repository/$fifo_name/nmon_data.dat";
|
||||
my $nmon_external_dat = "$APP_VAR/nmon_repository/$fifo_name/nmon_external.dat";
|
||||
my $nmon_external_header_dat =
|
||||
"$APP_VAR/nmon_repository/$fifo_name/nmon_external_header.dat";
|
||||
my $nmon_timestamp_dat =
|
||||
"$APP_VAR/nmon_repository/$fifo_name/nmon_timestamp.dat";
|
||||
my $nmon_error_dat = "$APP_VAR/nmon_repository/$fifo_name/nmon_error.dat";
|
||||
|
||||
my @nmon_dat = (
|
||||
"$nmon_config_dat", "$nmon_header_dat",
|
||||
"$nmon_data_dat", "$nmon_timestamp_dat",
|
||||
"$nmon_external_dat", "$nmon_external_header_dat",
|
||||
"$nmon_error_dat"
|
||||
);
|
||||
|
||||
my $file;
|
||||
my $rotated_file;
|
||||
|
||||
# Remove any existing rotated file
|
||||
foreach $file (@nmon_dat) {
|
||||
$rotated_file = "$file.rotated";
|
||||
if ( -e $rotated_file ) {
|
||||
unlink $rotated_file;
|
||||
}
|
||||
}
|
||||
|
||||
# Manage existing files and do the rotation if required
|
||||
if ( !-z $nmon_data_dat ) {
|
||||
foreach $file (@nmon_dat) {
|
||||
$rotated_file = "$file.rotated";
|
||||
move( $file, $rotated_file );
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach $file (@nmon_dat) {
|
||||
if ( -e $file ) {
|
||||
unlink $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################
|
||||
############# Main Program
|
||||
####################################################################
|
||||
|
||||
if ( !-p $fifo_path ) {
|
||||
print(
|
||||
"\n$time INFO: The fifo file $fifo_path does not exist yet, we are not ready to start.\n"
|
||||
);
|
||||
exit 0;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
my $fifoh;
|
||||
|
||||
# Open the named pipe "a la shell" to ensure that we we will quite when the nmon process has ended as well
|
||||
open( $fifoh, "$APP/bin/metricator_reader.sh $fifo_path|" );
|
||||
|
||||
while (<$fifoh>) {
|
||||
chomp($_);
|
||||
|
||||
my $nmon_config_match = '^[AAA|BBB].+';
|
||||
my $nmon_header_match =
|
||||
'^(?!AAA|BBB|TOP)[a-zA-Z0-9\-\_]*,(?!T\d{3,})[^,]*,(?!T\d{3,})[^,]*.*';
|
||||
my $nmon_header_TOP_match = '^TOP,(?!\d*,)';
|
||||
my $nmon_timestamp_match = '^ZZZZ,T\d*';
|
||||
my $nmon_error_match = '^ERROR,T\d*';
|
||||
|
||||
if ( $_ =~ /$nmon_config_match/ ) {
|
||||
open( my $fh, '>>', $nmon_config_dat )
|
||||
or die "Could not open file '$nmon_config_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
elsif ( $_ =~ /$nmon_header_match/ ) {
|
||||
open( my $fh, '>>', $nmon_header_dat )
|
||||
or die "Could not open file '$nmon_header_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
elsif ( $_ =~ /$nmon_header_TOP_match/ ) {
|
||||
open( my $fh, '>>', $nmon_header_dat )
|
||||
or die "Could not open file '$nmon_header_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
elsif ( $_ =~ /$nmon_error_match/ ) {
|
||||
open( my $fh, '>>', $nmon_error_dat )
|
||||
or die "Could not open file '$nmon_error_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
elsif ( $_ =~ /$nmon_timestamp_match/ ) {
|
||||
open( my $fh, '>>', $nmon_timestamp_dat )
|
||||
or die "Could not open file '$nmon_timestamp_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
open( my $fh, '>>', $nmon_data_dat )
|
||||
or die "Could not open file '$nmon_data_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
else {
|
||||
open( my $fh, '>>', $nmon_data_dat )
|
||||
or die "Could not open file '$nmon_data_dat' $!";
|
||||
print $fh "$_\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
}
|
||||
close $fifoh;
|
||||
exit(0);
|
||||
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
# Program name: metricator_reader.py
|
||||
# Compatibility: Python 3.x
|
||||
# Purpose - read nmon data from fifo file
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
import os
|
||||
import sys
|
||||
import optparse
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
# script version
|
||||
version = '3.0.0'
|
||||
|
||||
#################################################
|
||||
# Variables
|
||||
#################################################
|
||||
|
||||
# Set logging format
|
||||
logging.root
|
||||
logging.root.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(levelname)s %(message)s')
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
logging.root.addHandler(handler)
|
||||
|
||||
# Verify SPLUNK_HOME environment variable is available, the script is expected to be launched by Splunk
|
||||
# which will set this.
|
||||
# for debugging or manual run, please set this variable manually
|
||||
try:
|
||||
os.environ["SPLUNK_HOME"]
|
||||
except KeyError:
|
||||
logging.error(
|
||||
'The environment variable SPLUNK_HOME could not be verified, if you want to run this script manually you need'
|
||||
' to export it before processing')
|
||||
sys.exit(1)
|
||||
|
||||
# SPLUNK_HOME environment variable
|
||||
SPLUNK_HOME = os.environ['SPLUNK_HOME']
|
||||
|
||||
# APP_VAR directory
|
||||
APP_VAR = SPLUNK_HOME + '/var/log/metricator/var'
|
||||
if not os.path.exists(APP_VAR):
|
||||
logging.info(
|
||||
'The application var directory does not exist yet, we are not ready to start')
|
||||
sys.exit(0)
|
||||
|
||||
# APP Directories for TA-metricator-hec-for-nmon
|
||||
TA_NMON_APP = SPLUNK_HOME + '/etc/apps/TA-metricator-hec-for-nmon'
|
||||
TA_NMON_APP_CLUSTERED = SPLUNK_HOME + '/etc/slave-apps/TA-metricator-hec-for-nmon'
|
||||
|
||||
# Empty APP
|
||||
APP = ''
|
||||
|
||||
# Verify APP exist
|
||||
if os.path.exists(TA_NMON_APP):
|
||||
APP = TA_NMON_APP
|
||||
elif os.path.exists(TA_NMON_APP_CLUSTERED):
|
||||
APP = TA_NMON_APP_CLUSTERED
|
||||
else:
|
||||
msg = 'The Application root directory could not be found, is the TA-metricator-hec-for-nmon ? We tried: ' + \
|
||||
str(TA_NMON_APP) + ' ' + str(TA_NMON_APP_CLUSTERED)
|
||||
logging.error(msg)
|
||||
sys.exit(1)
|
||||
|
||||
# metricator_reader.sh
|
||||
fifo_reader = APP + "/bin/metricator_reader.sh"
|
||||
|
||||
#################################################
|
||||
# Arguments
|
||||
#################################################
|
||||
|
||||
parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog '+version)
|
||||
|
||||
parser.add_option('-F', '--fifo', action='store', type='string', dest='fifo_name',
|
||||
help='set the fifo file to be read')
|
||||
parser.add_option('--dumpargs', action='store_true', dest='dumpargs',
|
||||
help='only dump the passed arguments and exit (for debugging purposes only)')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.dumpargs:
|
||||
print("options: ", options)
|
||||
print("args: ", args)
|
||||
sys.exit(0)
|
||||
|
||||
if not options.fifo_name:
|
||||
logging.error(
|
||||
'The fifo file option has not been set (-F fifo_name or --fifo fifo_name)')
|
||||
sys.exit(1)
|
||||
else:
|
||||
fifo_name = options.fifo_name
|
||||
|
||||
# define the full path to the fifo file
|
||||
fifo_path = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon.fifo'
|
||||
|
||||
# At startup, rotate any existing non empty .dat file if nmon_data.dat is not empty
|
||||
|
||||
# define the various files to be written
|
||||
|
||||
# realtime files
|
||||
nmon_config_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_config.dat'
|
||||
nmon_header_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_header.dat'
|
||||
nmon_data_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_data.dat'
|
||||
nmon_timestamp_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_timestamp.dat'
|
||||
nmon_external_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_external.dat'
|
||||
nmon_external_header_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_external_header.dat'
|
||||
nmon_error_dat = APP_VAR + '/nmon_repository/' + fifo_name + '/nmon_error.dat'
|
||||
nmon_dat = {nmon_config_dat, nmon_header_dat, nmon_timestamp_dat, nmon_data_dat, nmon_external_dat,
|
||||
nmon_external_header_dat, nmon_error_dat}
|
||||
|
||||
# Manage existing files and do the rotation if required
|
||||
if os.path.exists(nmon_data_dat) and os.path.getsize(nmon_data_dat) > 0:
|
||||
for file in nmon_dat:
|
||||
rotated_file = str(file) + ".rotated"
|
||||
if os.path.isfile(rotated_file):
|
||||
os.remove(rotated_file)
|
||||
if os.path.isfile(file):
|
||||
os.rename(file, rotated_file)
|
||||
|
||||
elif os.path.exists(nmon_data_dat):
|
||||
for file in nmon_dat:
|
||||
if os.path.isfile(file):
|
||||
os.remove(file)
|
||||
|
||||
####################################################################
|
||||
# Main Program
|
||||
####################################################################
|
||||
|
||||
# Verify the fifo file exists, and start processing
|
||||
if not os.path.exists(fifo_path):
|
||||
logging.info(
|
||||
'The fifo file ' + fifo_path + ' does not exist yet, we are not ready to start')
|
||||
sys.exit(0)
|
||||
else:
|
||||
# we use the metricator_reader.sh to read the fifo file, benchmarks have shown more stability than
|
||||
# opening the fifo file in pure Python
|
||||
cmd = fifo_reader + " " + fifo_path
|
||||
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
|
||||
|
||||
while 1:
|
||||
line = str(p.stdout.readline())
|
||||
if line == '' and p.poll() != None:
|
||||
break
|
||||
|
||||
# Manage nmon config
|
||||
nmon_config_match = re.match(r'^[AAA|BBB].+', str(line))
|
||||
nmon_header_match = re.match(r'^(?!AAA|BBB|TOP)[a-zA-Z0-9\-\_]*,(?!T\d{3,})[^,]*,(?!T\d{3,})[^,]*.*', str(line))
|
||||
nmon_header_TOP_match = re.match(r'^TOP,(?!\d*,)', str(line))
|
||||
nmon_timestamp_match = re.match(r'^ZZZZ,T\d*', str(line))
|
||||
nmon_error_match = re.match(r'^ERROR,T\d*', str(line))
|
||||
|
||||
if nmon_config_match:
|
||||
with open(nmon_config_dat, "a") as nmon_config:
|
||||
print(str(line))
|
||||
nmon_config.write(line)
|
||||
|
||||
elif nmon_header_match:
|
||||
with open(nmon_header_dat, "a") as nmon_header:
|
||||
nmon_header.write(line)
|
||||
|
||||
elif nmon_header_TOP_match:
|
||||
with open(nmon_header_dat, "a") as nmon_header:
|
||||
nmon_header.write(line)
|
||||
|
||||
elif nmon_error_match:
|
||||
with open(nmon_error_dat, "a") as nmon_error:
|
||||
nmon_error.write(line)
|
||||
|
||||
# timestamp management: write the nmon timestamp in nmon_data and as well nmon_timestamp for later use
|
||||
elif nmon_timestamp_match:
|
||||
with open(nmon_timestamp_dat, "a") as nmon_timestamp:
|
||||
nmon_timestamp.write(line)
|
||||
with open(nmon_data_dat, "a") as nmon_data:
|
||||
nmon_data.write(line)
|
||||
|
||||
else:
|
||||
with open(nmon_data_dat, "a") as nmon_data:
|
||||
nmon_data.write(line)
|
||||
@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
# set -x
|
||||
|
||||
# Program name: metricator_reader.sh
|
||||
# Compatibility: Shell
|
||||
# Purpose - read nmon data from fifo file and output to stdout
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
# Version 2.0.0
|
||||
|
||||
# For AIX / Linux / Solaris
|
||||
|
||||
#################################################
|
||||
## Your Customizations Go Here ##
|
||||
#################################################
|
||||
|
||||
# fifo to be read (valid choices are: fifo1 | fifo2
|
||||
FIFO=$1
|
||||
|
||||
####################################################################
|
||||
############# Main Program ############
|
||||
####################################################################
|
||||
|
||||
while IFS= read line
|
||||
do
|
||||
echo $line
|
||||
done <$FIFO
|
||||
|
||||
exit 0
|
||||
@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Program name: nmon_external_snap.sh
|
||||
# Purpose - Add external command results to extend nmon data
|
||||
# Author - Guilhem Marchand
|
||||
# Disclaimer: this provided "as is".
|
||||
# Date - March 2017
|
||||
# Guilhem Marchand 2017/03/18, initial version
|
||||
|
||||
# 2017/04/29, Guilhem Marchand: - AIX compatibility issues, detach the commands in background
|
||||
# 2017/06/04, Guilhem Marchand: - Manage nmon external data in a dedicated file
|
||||
# 2018/01/09, Guilhem Marchand: - exclude dockers virtual fs, improve exclusion of false fs
|
||||
|
||||
# Version 1.0.3
|
||||
|
||||
# For AIX / Linux / Solaris
|
||||
|
||||
# for more information, see:
|
||||
# https://www.ibm.com/developerworks/community/blogs/aixpert/entry/nmon_and_External_Data_Collectors?lang=en
|
||||
|
||||
# This script will output the values for our custom external monitors
|
||||
# The first field defines the name of the monitor (type field in the application)
|
||||
# This monitor name must then be added to your local/nmonparser_config.json file
|
||||
|
||||
# 2 sections are available for nmon external monitor managements:
|
||||
# - nmon_external: manage any number of fields without transposition
|
||||
# - nmon_external_transposed: manage any number of fields with a notion of device / value
|
||||
|
||||
# note: the NMON_FIFO_PATH is a pattern that will be replaced by the metricator_helper.sh script in a copy of this script
|
||||
# that lives for the time to live of the nmon process started
|
||||
|
||||
# CAUTION: ensure your custom command does not output any comma within the field name and value
|
||||
|
||||
# Number of running processes
|
||||
echo "PROCCOUNT,$1,`ps -ef | wc -l`" >>NMON_FIFO_PATH/nmon_external.dat &
|
||||
|
||||
# Uptime information (uptime command output)
|
||||
echo "UPTIME,$1,\"`uptime | sed 's/^\s//g' | sed 's/,/;/g'`\"" >>NMON_FIFO_PATH/nmon_external.dat &
|
||||
|
||||
# df table information
|
||||
DF_TABLE=`df -k -P | sed '1d' | egrep -v '\/proc$|/dev$|\/run$|^tmpfs.*\/dev.*$|^tmpfs.*\/run.*$|^tmpfs.*\/sys.*$|^tmpfs.*\/var.*$|^none.*\/run|^none.*\/sys.*|\/var\/lib\/docker\/aufs\/mnt\/.*|\/var\/lib\/docker\/containers\/.*' | awk '{print $6}'`
|
||||
for fs in $DF_TABLE; do
|
||||
echo "DF_STORAGE,$1,`df -k -P $fs | sed '1d' | sed 's/%//g' | sed 's/,/;/g' | awk '{print $1 "," $2 "," $3 "," $4 "," $5 "," $6}'`" >>NMON_FIFO_PATH/nmon_external.dat
|
||||
done
|
||||
|
||||
# DF_INODES, for AIX and Linux
|
||||
case `uname` in
|
||||
"AIX")
|
||||
for fs in $DF_TABLE; do
|
||||
echo "DF_INODES,$1,`df -i $fs | sed '1d' | sed 's/%//g' | sed 's/,/;/g' | awk '{print $1 "," $5 "," $6 "," $7}'`" >>NMON_FIFO_PATH/nmon_external.dat
|
||||
done
|
||||
;;
|
||||
"Linux")
|
||||
for fs in $DF_TABLE; do
|
||||
echo "DF_INODES,$1,`df -i -P $fs | sed '1d' | sed 's/%//g' | sed 's/,/;/g' | awk '{print $1 "," $2 "," $3 "," $4 "," $5 "," $6}'`" >>NMON_FIFO_PATH/nmon_external.dat
|
||||
done
|
||||
;;
|
||||
esac
|
||||
@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Program name: nmon_external_start.sh
|
||||
# Purpose - Add external command results to extend nmon data (header definition)
|
||||
# Author - Guilhem Marchand
|
||||
# Disclaimer: this provided "as is".
|
||||
# Date - March 2017
|
||||
# Guilhem Marchand 2017/03/18, initial version
|
||||
# Guilhem Marchand 2017/03/29, protect against unexpecting failure in NMON_EXTERNAL_DIR getting value
|
||||
# Guilhem Marchand 2017/06/09, use dedicated files for external header and data
|
||||
# Guilhem Marchand 2017/08/17, Adding DF table
|
||||
|
||||
# Version 1.0.3
|
||||
|
||||
# For AIX / Linux / Solaris
|
||||
|
||||
# for more information, see:
|
||||
# https://www.ibm.com/developerworks/community/blogs/aixpert/entry/nmon_and_External_Data_Collectors?lang=en
|
||||
|
||||
# This script will define the headers for our custom external monitors
|
||||
# The first field defines the name of the monitor (type field in the application)
|
||||
# This monitor name must then be added to your local/nmonparser_config.json file
|
||||
|
||||
# 2 sections are available for nmon external monitor managements:
|
||||
# - nmon_external: manage any number of fields without transposition
|
||||
# - nmon_external_transposed: manage any number of fields with a notion of device / value
|
||||
|
||||
# note: the NMON_FIFO_PATH is a pattern that will be replaced by the metricator_helper.sh script in a copy of this script
|
||||
# that lives for the time to live of the nmon process started
|
||||
|
||||
# Empty the header file if existing
|
||||
if [ -f NMON_FIFO_PATH/nmon_external_header.dat ]; then
|
||||
>NMON_FIFO_PATH/nmon_external_header.dat
|
||||
fi
|
||||
|
||||
# CAUTION: ensure your custom command does not output any comma within the field name and value
|
||||
|
||||
# number of running processes
|
||||
echo "PROCCOUNT,Process Count,nb_running_processes" >>NMON_FIFO_PATH/nmon_external_header.dat
|
||||
|
||||
# uptime information
|
||||
echo "UPTIME,Server Uptime and load,uptime_stdout" >>NMON_FIFO_PATH/nmon_external_header.dat
|
||||
|
||||
# DF table (file systems usage)
|
||||
echo "DF_STORAGE,File system disk space usage,filesystem,blocks,Used,Available,Use_pct,mount" >>NMON_FIFO_PATH/nmon_external_header.dat
|
||||
# inodes utilization table is system specific and available for AIX and Linux
|
||||
case `uname` in
|
||||
"AIX")
|
||||
echo "DF_INODES,File system inodes usage,filesystem,IUsed,IUse_pct,mount" >>NMON_FIFO_PATH/nmon_external_header.dat ;;
|
||||
"Linux")
|
||||
echo "DF_INODES,File system inodes usage,filesystem,Inodes,IUsed,IFree,IUse_pct,mount" >>NMON_FIFO_PATH/nmon_external_header.dat ;;
|
||||
esac
|
||||
@ -0,0 +1,197 @@
|
||||
#!/bin/sh
|
||||
|
||||
# set -x
|
||||
|
||||
# Program name: nmonparser.sh
|
||||
# Purpose - Frontal script to nmonparser, will launch Python or Perl script depending on interpreter availability
|
||||
# See nmonparser | nmonparser.pl
|
||||
# Author - Guilhem Marchand
|
||||
|
||||
# Version 2.0.1
|
||||
|
||||
# For AIX / Linux / Solaris
|
||||
|
||||
#################################################
|
||||
## Your Customizations Go Here ##
|
||||
#################################################
|
||||
|
||||
# format date output to strftime dd/mm/YYYY HH:MM:SS
|
||||
log_date () {
|
||||
date "+%d-%m-%Y %H:%M:%S"
|
||||
}
|
||||
|
||||
# Set host
|
||||
HOST=`hostname`
|
||||
|
||||
if [ -z "${SPLUNK_HOME}" ]; then
|
||||
echo "`log_date`, ERROR, SPLUNK_HOME variable is not defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set tmp directory
|
||||
APP_VAR=${SPLUNK_HOME}/var/log/metricator
|
||||
|
||||
# Verify it exists
|
||||
if [ ! -d ${APP_VAR} ]; then
|
||||
mkdir -p ${APP_VAR}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# silently remove tmp file (testing exists before rm seems to cause trouble on some old OS)
|
||||
rm -f ${APP_VAR}/nmonparser.temp.*
|
||||
|
||||
# Set nmon_temp
|
||||
nmon_temp=${APP_VAR}/nmonparser.temp.$$
|
||||
|
||||
# APP path discovery
|
||||
if [ -d "$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon" ]; then
|
||||
APP=$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon
|
||||
|
||||
elif [ -d "$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon" ];then
|
||||
APP=$SPLUNK_HOME/etc/slave-apps/TA-metricator-hec-for-nmon
|
||||
|
||||
else
|
||||
echo "`log_date`, ${HOST} ERROR, the APP directory could not be defined, is the TA-metricator-hec-for-nmon installed ?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# source default nmon.conf
|
||||
if [ -f $APP/default/nmon.conf ]; then
|
||||
# During initial deployment, the nmon.conf needs to be managed properly by the metricator_consumer.sh
|
||||
# wait for this to be done
|
||||
grep '\[nmon\]' $APP/default/nmon.conf >/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "`log_date`, ${HOST} INFO, initial deployment condition detected, safe exiting."
|
||||
exit 0
|
||||
else
|
||||
. $APP/default/nmon.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
# source local nmon.conf, if any
|
||||
|
||||
# Search for a local nmon.conf file located in $SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local
|
||||
if [ -f $APP/local/nmon.conf ]; then
|
||||
. $APP/local/nmon.conf
|
||||
fi
|
||||
|
||||
# On a per server basis, you can also set in /etc/nmon.conf
|
||||
if [ -f /etc/nmon.conf ]; then
|
||||
. /etc/nmon.conf
|
||||
fi
|
||||
|
||||
# Manage FQDN option
|
||||
echo $nmonparser_options | grep '\-\-use_fqdn' >/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
# Only relevant for Linux OS
|
||||
case $UNAME in
|
||||
Linux)
|
||||
HOST=`hostname -f` ;;
|
||||
AIX)
|
||||
HOST=`hostname` ;;
|
||||
SunOS)
|
||||
HOST=`hostname` ;;
|
||||
esac
|
||||
else
|
||||
HOST=`hostname`
|
||||
fi
|
||||
|
||||
# Manage host override option based on Splunk hostname defined
|
||||
case $override_sys_hostname in
|
||||
"1")
|
||||
# Retrieve the Splunk host value
|
||||
HOST=`cat $SPLUNK_HOME/etc/system/local/inputs.conf | grep '^host =' | awk -F\= '{print $2}' | sed 's/ //g'`
|
||||
;;
|
||||
esac
|
||||
|
||||
#
|
||||
# Interpreter choice
|
||||
#
|
||||
|
||||
PYTHON=0
|
||||
PYTHON2=0
|
||||
PYTHON3=0
|
||||
PERL=0
|
||||
# Set the default interpreter
|
||||
INTERPRETER="python"
|
||||
|
||||
# Get the version for both worlds
|
||||
PYTHON2=`which python 2>&1`
|
||||
PYTHON3=`which python3 2>&1`
|
||||
PERL=`which perl 2>&1`
|
||||
|
||||
# Handle Python
|
||||
PYTHON_available="false"
|
||||
case $PYTHON3 in
|
||||
*python*)
|
||||
PYTHON_available="true"
|
||||
INTERPRETER="python3" ;;
|
||||
*)
|
||||
case $PYTHON2 in
|
||||
*python*)
|
||||
PYTHON_available="true"
|
||||
INTERPRETER="python" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# Handle Perl
|
||||
case $PERL in
|
||||
*perl*)
|
||||
PERL_available="true"
|
||||
;;
|
||||
*)
|
||||
PERL_available="false"
|
||||
;;
|
||||
esac
|
||||
|
||||
case `uname` in
|
||||
|
||||
# AIX priority is Perl
|
||||
"AIX")
|
||||
case $PERL_available in
|
||||
"true")
|
||||
INTERPRETER="perl" ;;
|
||||
"false")
|
||||
INTERPRETER="$INTERPRETER" ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
# Other OS, priority is Python
|
||||
*)
|
||||
case $PYTHON_available in
|
||||
"true")
|
||||
INTERPRETER="$INTERPRETER" ;;
|
||||
"false")
|
||||
INTERPRETER="perl" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
####################################################################
|
||||
############# Main Program ############
|
||||
####################################################################
|
||||
|
||||
# Store arguments sent to script
|
||||
userargs=$@
|
||||
|
||||
# Store stdin
|
||||
while read line ; do
|
||||
echo "$line" >> ${nmon_temp}
|
||||
done
|
||||
|
||||
# Start the parser
|
||||
case ${INTERPRETER} in
|
||||
|
||||
"python"|"python3")
|
||||
cat ${nmon_temp} | ${SPLUNK_HOME}/bin/splunk cmd $INTERPRETER ${APP}/bin/nmonparser_hec.py ${userargs} ;;
|
||||
|
||||
"perl")
|
||||
cat ${nmon_temp} | ${SPLUNK_HOME}/bin/splunk cmd ${APP}/bin/nmonparser_hec.pl ${userargs} ;;
|
||||
|
||||
esac
|
||||
|
||||
# Remove temp
|
||||
rm -f ${nmon_temp}
|
||||
|
||||
exit 0
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,140 @@
|
||||
# pre-actions scripts
|
||||
|
||||
The frameID definition is an enrichment mechanism used within the application to associate a given host with a given frame identifier.
|
||||
By default, the mapping is operated against the value of "serialnum" which is defined at the raw level by nmon binaries.
|
||||
|
||||
- On AIX systems, the serialnum value is equal to the serial number of the frame hosting the partition
|
||||
- On Linux and Solaris systems, the serialnum is equal to the value of the hostname
|
||||
|
||||
A pre-action script can be designed to define a serial number according to your needs.
|
||||
|
||||
In "nmon.conf", the following settings are designed to manage the serial number:
|
||||
|
||||
- override_sys_serialnum="1": will activate the serial number override
|
||||
- override_sys_serialnum_value="<value for serial number": defines it value
|
||||
|
||||
Add any shell script in this directory to get a pre-action to be executed automatically by the metricator_helper.sh script at startup time.
|
||||
You can use this simple feature to perform a pre-action each time the metricator_helper.sh script is executed.
|
||||
|
||||
stdout will be indexed in "sourcetype=nmon_collect".
|
||||
stderr will be indexes in splunkd logs.
|
||||
|
||||
Any script you would add in this directory will be upgrade resilient and would not be lost or modified when your upgrade the TA.
|
||||
|
||||
pre-action scripts execution will be visible in sourcetype=nmon_collect:
|
||||
|
||||
message = <date>, ${HOST} INFO, executing pre-action script: <name of pre-action script>
|
||||
|
||||
Requirements:
|
||||
|
||||
- scripts names can whatever you want
|
||||
- must have ".sh" extension
|
||||
- must have execution permission by the Unix username owning processes
|
||||
|
||||
## use case examples:
|
||||
|
||||
### 1. run a local command on servers to define the serial number value
|
||||
|
||||
----------------------------
|
||||
#!/bin/sh
|
||||
|
||||
serialnumber=`<replace the command that retrieves a value to be used as the serialnumber`
|
||||
|
||||
# nmon_conf="/etc/nmon.conf" to write in /etc/nmon.conf (requires processes to run under root)
|
||||
# nmon_conf="$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local/nmon.conf" to write in app name space
|
||||
nmon_conf="$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local/nmon.conf"
|
||||
|
||||
# if nmon.conf could not be found, create, activate serial number override and fill its value
|
||||
|
||||
if [ ! -f $nmon_conf ]; then
|
||||
echo "# nmon.conf" >> $nmon_conf
|
||||
echo "override_sys_serialnum=\"1\"" >> $nmon_conf
|
||||
echo "override_sys_serialnum_value=\"$serialnumber\"" >> $nmon_conf
|
||||
|
||||
else # verify the option activation, verify the serial number value
|
||||
|
||||
egrep "^override_sys_serialnum=" $nmon_conf >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "override_sys_serialnum=\"1\"" >> $nmon_conf
|
||||
fi
|
||||
|
||||
# verify serial number value
|
||||
egrep "^override_sys_serialnum_value=" $nmon_conf >/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then # if option is set, check value
|
||||
egrep "^override_sys_serialnum_value=\"$serialnumber\"" $nmon_conf >/dev/null
|
||||
if [ $? -ne 0 ]; then # if mismatch, replace value
|
||||
cat $nmon_conf | grep -v "override_sys_serialnum_value" > ${nmon_conf}.new
|
||||
echo "override_sys_serialnum_value=\"$serialnumber\"" >> ${nmon_conf}.new
|
||||
mv ${nmon_conf}.new $nmon_conf
|
||||
fi
|
||||
else # option is not set, simply add it
|
||||
echo "override_sys_serialnum_value=\"$serialnumber\"" >> $nmon_conf
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
exit 0
|
||||
----------------------------
|
||||
|
||||
### 2. Using servers naming convention and domain names to setup a frameID by extraction the region name
|
||||
|
||||
----------------------------
|
||||
#!/bin/sh
|
||||
|
||||
# convention naming: server001.mycompany.co.uk | retrieve the region being the 4th segment of FQDN
|
||||
HOST=`hostname`
|
||||
case `uname` in
|
||||
Linux)
|
||||
FQDN=`hostname -f` ;;
|
||||
*)
|
||||
FQDN=$HOST ;;
|
||||
esac
|
||||
|
||||
REGION=`echo $FQDN | awk -F\. '{print $4}'`
|
||||
|
||||
# revert to host value in case of failure
|
||||
# otherwise, affect to: DC-<region>
|
||||
case $REGION in
|
||||
"")
|
||||
serialnumber="$HOST" ;;
|
||||
*)
|
||||
serialnumber="datacenter-$REGION" ;;
|
||||
esac
|
||||
|
||||
# nmon_conf="/etc/nmon.conf" to write in /etc/nmon.conf (requires processes to run under root)
|
||||
# nmon_conf="$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local/nmon.conf" to write in app name space
|
||||
nmon_conf="$SPLUNK_HOME/etc/apps/TA-metricator-hec-for-nmon/local/nmon.conf"
|
||||
|
||||
# if nmon.conf could not be found, create, activate serial number override and fill its value
|
||||
|
||||
if [ ! -f $nmon_conf ]; then
|
||||
echo "# nmon.conf" >> $nmon_conf
|
||||
echo "override_sys_serialnum=\"1\"" >> $nmon_conf
|
||||
echo "override_sys_serialnum_value=\"$serialnumber\"" >> $nmon_conf
|
||||
|
||||
else # verify the option activation
|
||||
|
||||
egrep "^override_sys_serialnum=" $nmon_conf >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "override_sys_serialnum=\"1\"" >> $nmon_conf
|
||||
fi
|
||||
|
||||
# verify serial number value
|
||||
egrep "^override_sys_serialnum_value=" $nmon_conf >/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then # if option is set, check value
|
||||
egrep "^override_sys_serialnum_value=\"$serialnumber\"" $nmon_conf >/dev/null
|
||||
if [ $? -ne 0 ]; then # if mismatch, replace value
|
||||
cat $nmon_conf | grep -v "override_sys_serialnum_value" > ${nmon_conf}.new
|
||||
echo "override_sys_serialnum_value=\"$serialnumber\"" >> ${nmon_conf}.new
|
||||
mv ${nmon_conf}.new $nmon_conf
|
||||
fi
|
||||
else # option is not set, simply add it
|
||||
echo "override_sys_serialnum_value=\"$serialnumber\"" >> $nmon_conf
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
exit 0
|
||||
----------------------------
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
# sarmon_bin_* directories contain compiled sarmon binaries for x86 and sparc processor
|
||||
|
||||
The source code of sarmon is available at the sarmon web site: http://www.geckotechnology.com/sarmon
|
||||
@ -0,0 +1,19 @@
|
||||
#
|
||||
# Splunk app configuration file
|
||||
#
|
||||
|
||||
[install]
|
||||
is_configured = 0
|
||||
|
||||
[package]
|
||||
id = TA-metricator-hec-for-nmon
|
||||
check_for_updates = true
|
||||
|
||||
[ui]
|
||||
is_visible = 0
|
||||
label = TA-metricator-hec-for-nmon
|
||||
|
||||
[launcher]
|
||||
author = Guilhem Marchand for Octamis
|
||||
description = Nmon performance and configuration data technical addon for the metric store
|
||||
version = 1.1.1
|
||||
@ -0,0 +1,38 @@
|
||||
# inputs.conf
|
||||
|
||||
######
|
||||
# main
|
||||
######
|
||||
|
||||
# The metricator_helper.sh performs nmon binary starting if required
|
||||
# by default, the script is executed every 60 seconds and will not perform any action if
|
||||
# the nmon binary is started
|
||||
|
||||
[script://./bin/metricator_helper.sh]
|
||||
disabled = false
|
||||
index = os-unix-nmon-internal
|
||||
interval = 60
|
||||
source = nmon_collect
|
||||
sourcetype = nmon_collect
|
||||
|
||||
# The metricator_consumer.sh script consumes nmon data produced in fifo mode
|
||||
|
||||
[script://./bin/metricator_consumer.sh]
|
||||
disabled = false
|
||||
index = os-unix-nmon-internal
|
||||
interval = 60
|
||||
source = fifo_consumer
|
||||
sourcetype = nmon_processing
|
||||
|
||||
##############
|
||||
# nmon cleaner
|
||||
##############
|
||||
|
||||
# The metricator_cleaner.sh script performs cleaning tasks internal to the TA, scheduled once a day
|
||||
|
||||
[script://./bin/metricator_cleaner.sh --cleancsv]
|
||||
disabled = false
|
||||
index = os-unix-nmon-internal
|
||||
interval = 14400
|
||||
source = nmon_cleaner
|
||||
sourcetype = nmon_clean
|
||||
@ -0,0 +1,233 @@
|
||||
# nmon.conf
|
||||
|
||||
|
||||
|
||||
################################
|
||||
# Nmon processes related options
|
||||
################################
|
||||
|
||||
#
|
||||
# These options will be used to manage Nmon processes starting options
|
||||
#
|
||||
|
||||
# This configuration file will set the interval and snapshot values when starting up the nmon binary
|
||||
# It is being sourced by the metricator_helper.sh script during script startup
|
||||
|
||||
# *** BE UPGRADE RESILIENT: *** Copy this file to your local/nmon.conf to prevent future upgrades from overwriting your settings
|
||||
# *** DON'T MODIFY THIS FILE ***
|
||||
|
||||
# *** FILE ENCODING: UTF-8 ! ***
|
||||
# When creating a local/nmon.conf, pay attention to file encoding specially when working under Windows.
|
||||
# The file must be UTF-8 encoded or you may run in trouble.
|
||||
|
||||
### NMON COLLECT OPTIONS ###
|
||||
|
||||
# The metricator_helper.sh input script is set by default to run every 60 seconds
|
||||
# If Nmon is not running, the script will start Nmon using the configuration above
|
||||
|
||||
###
|
||||
### FIFO options:
|
||||
###
|
||||
|
||||
# Using FIFO files (named pipe) are now used to minimize the CPU footprint of the technical addons
|
||||
# As such, it is not required anymore to use short cycle of Nmon run to reduce the CPU usage
|
||||
|
||||
# You can still want to manage the volume of data to be generated by managing the interval and snapshot values
|
||||
# as a best practice recommendation, the time to live of nmon processes writing to FIFO should be 24 hours
|
||||
|
||||
# value for interval: time in seconds between 2 performance measures
|
||||
fifo_interval="60"
|
||||
|
||||
# value for snapshot: number of measure to perform
|
||||
fifo_snapshot="1440"
|
||||
|
||||
### VARIOUS COMMON OPTIONS ###
|
||||
|
||||
# Time in seconds of margin before running a new iteration of Nmon process to prevent data gaps between 2 iterations of Nmon
|
||||
# the metricator_helper.sh script will spawn a new Nmon process when the age in seconds of the current process gets higher than this value
|
||||
|
||||
# The endtime is evaluated the following way:
|
||||
# endtime=$(( ${interval} * ${snapshot} - ${endtime_margin} ))
|
||||
|
||||
# When the endtime gets higher than the endtime_margin, a new Nmon process will be spawned
|
||||
# default value to 240 seconds which will start a new process 4 minutes before the current process ends
|
||||
|
||||
# Setting this value to "0" will totally disable this feature
|
||||
|
||||
endtime_margin="240"
|
||||
|
||||
### NFS OPTIONS ###
|
||||
|
||||
# Change to "1" to activate NFS V2 / V3 (option -N) for AIX hosts
|
||||
AIX_NFS23="0"
|
||||
|
||||
# Change to "1" to activate NFS V4 (option -NN) for AIX hosts
|
||||
AIX_NFS4="0"
|
||||
|
||||
# Change to "1" to activate NFS V2 / V3 / V4 (option -N) for Linux hosts
|
||||
# Note: Some versions of Nmon introduced a bug that makes Nmon to core when activating NFS, ensure your version is not outdated
|
||||
Linux_NFS="0"
|
||||
|
||||
### LINUX OPTIONS ###
|
||||
|
||||
# Change the priority applied while looking at nmon binary
|
||||
# by default, the metricator_helper.sh script will use any nmon binary found in PATH
|
||||
# Set to "1" to give the priority to embedded nmon binaries
|
||||
# Note: Since release 1.6.07, priority is given by default to embedded binaries
|
||||
Linux_embedded_nmon_priority="1"
|
||||
|
||||
# Change the limit for processes and disks capture of nmon for Linux
|
||||
# In default configuration, nmon will capture most of the process table by capturing main consuming processes
|
||||
# This function is percentage limit of CPU time, with a default limit of 0.01
|
||||
# Changing this value can influence the volume of data to be generated, and the associated CPU overhead for that data to be parsed
|
||||
|
||||
# Possible values are:
|
||||
# Linux_unlimited_capture="0" --> Default nmon behavior, capture main processes (no -I option)
|
||||
# Linux_unlimited_capture="-1" --> Set the capture mode to unlimited (-I -1)
|
||||
# Linux_unlimited_capture="x.xx" --> Set the percentage limit to a custom value, ex: "0.01" will set "-I 0.01"
|
||||
Linux_unlimited_capture="0"
|
||||
|
||||
# Set the maximum number of devices collected by Nmon, default is set to 1500 devices
|
||||
# This option will be ignored if you set the Linux_unlimited_capturation below.
|
||||
# Increase this value if you have systems with more devices
|
||||
# Up to 3000 devices will be taken in charge by the Application (hard limit in nmonparser)
|
||||
Linux_devices="1500"
|
||||
|
||||
# Enable disks extended statistics (DG*)
|
||||
# Default is true, which activates and generates DG statistics
|
||||
Linux_disk_dg_enable="1"
|
||||
|
||||
# Name of the User Defined Disk Groups file, "auto" generates this for you
|
||||
Linux_disk_dg_group="auto"
|
||||
|
||||
### SOLARIS OPTIONS ###
|
||||
|
||||
# Change to "1" to activate VxVM volumes IO statistics
|
||||
Solaris_VxVM="0"
|
||||
|
||||
# UARG collection (new in Version 1.11), Change to "0" to deactivate, "1" to activate (default is activate)
|
||||
Solaris_UARG="1"
|
||||
|
||||
### AIX COMMON OPTIONS ###
|
||||
|
||||
# CAUTION: Since release 1.3.0, we use fifo files, which requires the option "-yoverwrite=1"
|
||||
|
||||
# Change this line if you add or remove common options for AIX, do not change NFS options here (see NFS options)
|
||||
# the -p option is mandatory as it is used at launch time to save instance pid
|
||||
AIX_options="-T -A -d -K -L -M -P -^ -p -yoverwrite=1"
|
||||
|
||||
# enable this line if you want to get only active disks
|
||||
# AIX_options=""-T -A -d -K -L -M -P -^ -p -k `lspv|grep active|awk '{print $1","}'|tr -d '\040\011\012\015'` -yoverwrite=1"
|
||||
|
||||
#############################
|
||||
# Application related options
|
||||
#############################
|
||||
|
||||
#
|
||||
# These options are not directly related to nmon processes but to general features of the technical add-on
|
||||
#
|
||||
|
||||
######################
|
||||
# hostname definition:
|
||||
######################
|
||||
|
||||
# This option can be used to force the technical add-on to use the Splunk configured value of the server hostname
|
||||
# If for some reason, you need to use the Splunk host value instead of the system real hostname value, set this value to "1"
|
||||
|
||||
# We will search for the value of host=<value> in $SPLUNK_HOME/etc/system/local/inputs.conf
|
||||
# If no value can be found, or if the file does not exist, we will fallback to the normal behavior
|
||||
|
||||
# Default is use system hostname
|
||||
|
||||
# FQDN management in nmonparser: The --fqdn option is not compatible with the host name override, if the override_sys_hostname
|
||||
# is activated, the --fqdn argument will have no effect
|
||||
|
||||
override_sys_hostname="0"
|
||||
|
||||
#####################
|
||||
# frameID definition:
|
||||
#####################
|
||||
|
||||
# The frameID definition is an enrichment mechanism used within the application to associate a given host with a given frame identifier
|
||||
# By default, the mapping is operated against the value of "serialnum" which is defined at the raw level by nmon binaries
|
||||
|
||||
# On AIX systems, the serialnum value is equal to the serial number of the frame hosting the partition
|
||||
# On Linux and Solaris systems, the serialnum is equal to the value of the hostname
|
||||
|
||||
# Using this option allows you to override the serialnum value by a static value defined in the nmon.conf configuration file
|
||||
# nmon.conf precedence allows defining the serialnum value on per deployment basis (local/nmon.conf) or on a per server basis (/etc/nmon.conf)
|
||||
|
||||
# default is:
|
||||
# override_sys_serialnum="0"
|
||||
# which lets nmon set the serialnum value
|
||||
|
||||
# Set this value to:
|
||||
# override_sys_serialnum="1"
|
||||
# to activate the serialnum override based on the value defined in:
|
||||
|
||||
# override_sys_serialnum_value="<sting>"
|
||||
# Acceptable values for <string> are letters (lower and upper case), numbers and "-" / "_"
|
||||
|
||||
override_sys_serialnum="0"
|
||||
override_sys_serialnum_value="none"
|
||||
|
||||
########################
|
||||
# nmon external metrics:
|
||||
########################
|
||||
|
||||
# nmon external generation management
|
||||
|
||||
# This option will manage the activation or deactivation of the nmon external data generation at the lower level, before it comes to parsers
|
||||
# default is activated (value=1), set to "0" to deactivate
|
||||
|
||||
nmon_external_generation="1"
|
||||
|
||||
###############
|
||||
# fifo options:
|
||||
###############
|
||||
|
||||
# Fifo options
|
||||
|
||||
# The realtime mode which corresponds to the old mechanism is now deprecated
|
||||
# fifo mode is mandatory
|
||||
|
||||
# Default is "1" which means write to fifo
|
||||
|
||||
mode_fifo="1"
|
||||
|
||||
#######################
|
||||
# nmon parsers options:
|
||||
#######################
|
||||
|
||||
# consult the documentation to get the full list of available options
|
||||
|
||||
# --mode fifo|colddata --> explicitly manage data in fifo/colddata
|
||||
# --use_fqdn --> use the host fully qualified domain name (default)
|
||||
# --silent --> minimize the processing output to save data volume (deactivated by default)
|
||||
# --show_zero_values --> allows generating metrics with 0 values (default removes any metric with a zero value before it reaches the ingestion)
|
||||
# --no_local_log --> do no write metrics, events and config locally on file-syste, (activated by default)
|
||||
# --splunk_http_url --> Splunk HEC endpoint URL (must contain the protocol, IP or FQDN and endpoint path)
|
||||
# --splunk_http_token --> Splunk HEC token value
|
||||
# --splunk_metrics_index --> Name of the metrics index (default: os-unix-nmon-metrics)
|
||||
# --splunk_events_index --> Name of the events index (default: os-unix-nmon-events)
|
||||
# --splunk_config_index --> Name of the config index (default: os-unix-nmon-config)
|
||||
|
||||
# In fifo mode, options are sent by the metricator_consumer.sh
|
||||
# In file mode, options are sent by Splunk via the nmon_processing stanza in props.conf
|
||||
|
||||
#
|
||||
# Splunk HEC configuration (http input)
|
||||
#
|
||||
|
||||
# Change the Splunk URL to match your protocol (http vs https) and your access URL
|
||||
# By default, as long the token value is not changed from the demonstration value above, the parser will just do nothing else than writing to local logs
|
||||
|
||||
# For more information, see: http://dev.splunk.com/view/event-collector/SP-CAAAE6M
|
||||
|
||||
# TO CONFIGURE:
|
||||
|
||||
# - create the "local" directory in /etc/nmon-logger
|
||||
# - copy the default/nmon.conf to local/
|
||||
# - manage your settings in your local nmon.conf
|
||||
|
||||
nmonparser_options="--mode fifo --use_fqdn --silent --no_local_log --splunk_http_url https://splunk.mydomain.com:8088/services/collector/event --splunk_http_token insert_your_splunk_http_token --splunk_metrics_index os-unix-nmon-metrics --splunk_events_index os-unix-nmon-events --splunk_config_index os-unix-nmon-config"
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"static_section":["CPUnn", "CPU_ALL", "FILE", "MEM", "PAGE", "MEMNEW", "MEMUSE", "PROC", "VM", "NFSSVRV2", "NFSSVRV3", "NFSSVRV4", "NFSCLIV2", "NFSCLIV3", "NFSCLIV4"],
|
||||
|
||||
"Solaris_static_section":["PROCSOL"],
|
||||
|
||||
"LPAR_static_section":["LPAR", "POOLS"],
|
||||
|
||||
"top_section":["TOP"],
|
||||
|
||||
"uarg_section":["UARG"],
|
||||
|
||||
"dynamic_section1":["DISKBUSY", "DISKBSIZE", "DISKREAD", "DISKWRITE", "DISKXFER", "DISKREADSERV", "DISKWRITESERV"],
|
||||
|
||||
"dynamic_section2":["IOADAPT", "NETERROR", "NET", "NETPACKET", "JFSFILE", "JFSINODE", "FCREAD", "FCWRITE", "FCXFERIN", "FCXFEROUT"],
|
||||
|
||||
"disk_extended_section":["DGBUSY", "DGREAD", "DGWRITE", "DGSIZE", "DGXFER", "DGREADS", "DGREADMERGE", "DGREADSERV", "DGWRITES", "DGWRITEMERGE", "DGWRITESERV", "DGINFLIGHT", "DGIOTIME", "DGBACKLOG"],
|
||||
|
||||
"solaris_WLM":["WLMPROJECTCPU", "WLMZONECPU", "WLMTASKCPU", "WLMUSERCPU", "WLMPROJECTMEM", "WLMZONEMEM", "WLMTASKMEM", "WLMUSERMEM"],
|
||||
|
||||
"solaris_VxVM":["VxVMREAD", "VxVMWRITE", "VxVMXFER", "VxVMBSIZE", "VxVMBUSY", "VxVMSVCTM", "VxVMWAITTM"],
|
||||
|
||||
"solaris_dynamic_various":["DISKSVCTM", "DISKWAITTM"],
|
||||
|
||||
"AIX_dynamic_various":["SEA", "SEAPACKET", "SEACHPHY"],
|
||||
|
||||
"AIX_WLM":["WLMCPU", "WLMMEM", "WLMBIO"],
|
||||
|
||||
"nmon_external":["UPTIME", "PROCCOUNT", "DF_STORAGE", "DF_INODES"],
|
||||
|
||||
"nmon_external_transposed":[""]
|
||||
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
# props.conf
|
||||
|
||||
###############################
|
||||
# nmon metrics for metric store
|
||||
###############################
|
||||
|
||||
# Introduced with Splunk 7, metrics are now natively supported
|
||||
# Nmon uses its own copy of the default metrics_csv sourcetype
|
||||
|
||||
[nmon_metrics_csv]
|
||||
SHOULD_LINEMERGE = False
|
||||
pulldown_type = true
|
||||
INDEXED_EXTRACTIONS = csv
|
||||
ADD_EXTRA_TIME_FIELDS = False
|
||||
KV_MODE = none
|
||||
TIMESTAMP_FIELDS = metric_timestamp
|
||||
TIME_FORMAT = %s.%Q
|
||||
category = Metrics
|
||||
description = Comma-separated value format for metrics. Nmon implementation.
|
||||
|
||||
# Overwritting default host field based on event data for nmon_data sourcetype (useful when managing Nmon central shares)
|
||||
TRANSFORMS-hostfield=nmon_metrics_csv_hostoverride
|
||||
|
||||
# Metrics can be sent by http using the Splunk Http Event Collector (HEC)
|
||||
[nmon_metrics_http]
|
||||
TIME_PREFIX = metric_timestamp=\"(\d+)\"
|
||||
TIME_FORMAT = %s
|
||||
TRANSFORMS-nmon_metrics_http = nmon_metrics_http_host, nmon_metrics_http_metric_name, nmon_metrics_http_metric_value, nmon_metrics_http_dims, nmon_metrics_http_OStype, nmon_metrics_http_serialnum
|
||||
NO_BINARY_CHECK = true
|
||||
SHOULD_LINEMERGE = false
|
||||
pulldown_type = 1
|
||||
category = Metrics
|
||||
|
||||
########################
|
||||
# nmon metrics as events
|
||||
########################
|
||||
|
||||
# This sourcetype stanza will be used to index nmon csv converted data
|
||||
# Every generated csv file will contain a CSV header used by Splunk to identify fields
|
||||
|
||||
[nmon_data]
|
||||
FIELD_DELIMITER=,
|
||||
FIELD_QUOTE="
|
||||
HEADER_FIELD_LINE_NUMBER=1
|
||||
|
||||
# your settings
|
||||
INDEXED_EXTRACTIONS=csv
|
||||
NO_BINARY_CHECK=1
|
||||
SHOULD_LINEMERGE=false
|
||||
TIMESTAMP_FIELDS=ZZZZ
|
||||
TIME_FORMAT=%d-%m-%Y %H:%M:%S
|
||||
|
||||
# set by detected source type
|
||||
KV_MODE=none
|
||||
pulldown_type=true
|
||||
|
||||
# Leaving PUNCT enabled can impact indexing performance, and uses space
|
||||
# For structured data, it has poor interest and shall be deactivated
|
||||
ANNOTATE_PUNCT=false
|
||||
|
||||
# Overwritting default host field based on event data for nmon_data sourcetype (useful when managing Nmon central shares)
|
||||
TRANSFORMS-hostfield=nmon_data_hostoverride
|
||||
|
||||
# nmon_data sent over http using the Splunk Http Event Collector (HEC)
|
||||
# This sourcetype will be automatically renamed to nmon_data
|
||||
|
||||
[nmon_data_http]
|
||||
SHOULD_LINEMERGE=false
|
||||
NO_BINARY_CHECK=true
|
||||
CHARSET=UTF-8
|
||||
TIME_FORMAT=%s
|
||||
TIME_PREFIX=timestamp="
|
||||
MAX_TIMESTAMP_LOOKAHEAD=26
|
||||
KV_MODE=auto
|
||||
|
||||
# Apply indexing time parsing configuration
|
||||
TRANSFORMS-nmon_data_http = nmon_data_http_host, nmon_data_http_OStype, nmon_data_http_type, nmon_data_http_sourcetype
|
||||
|
||||
# For search time extractions, activate kvmode to auto for that source
|
||||
[source::nmon_data:http]
|
||||
KV_MODE=auto
|
||||
|
||||
########################
|
||||
# nmon processing events
|
||||
########################
|
||||
|
||||
[nmon_processing]
|
||||
SHOULD_LINEMERGE=false
|
||||
NO_BINARY_CHECK=true
|
||||
CHARSET=UTF-8
|
||||
TIME_PREFIX=^
|
||||
TIME_FORMAT=%d-%m-%Y %H:%M:%S
|
||||
MAX_TIMESTAMP_LOOKAHEAD=19
|
||||
LINE_BREAKER=([\n\r]+)\d{2}-\d{2}-\d{4}\s\d{2}:\d{2}:\d{2}
|
||||
TRUNCATE=999999
|
||||
|
||||
# Deactivate KV
|
||||
KV_MODE=none
|
||||
|
||||
####################
|
||||
# nmon config events
|
||||
####################
|
||||
|
||||
[nmon_config]
|
||||
SHOULD_LINEMERGE=false
|
||||
NO_BINARY_CHECK=true
|
||||
CHARSET=UTF-8
|
||||
TIME_PREFIX=^CONFIG,
|
||||
TIME_FORMAT=%d-%b-%Y:%H:%M.%S
|
||||
LINE_BREAKER=([\r\n]+)CONFIG,\d{2}-\w{3}-\d{4}:\d{2}:\d{2}\.\d{2},
|
||||
TRUNCATE=0
|
||||
MAX_EVENTS=100000
|
||||
MAX_TIMESTAMP_LOOKAHEAD=30
|
||||
|
||||
# Deactivate KV
|
||||
KV_MODE = none
|
||||
|
||||
# Overwritting default host field based on event data for nmon_data sourcetype (useful when managing Nmon central shares)
|
||||
TRANSFORMS-hostfield=nmon_config_hostoverride
|
||||
|
||||
# nmon_config sent over http
|
||||
[nmon_config:http]
|
||||
SHOULD_LINEMERGE=false
|
||||
NO_BINARY_CHECK=true
|
||||
CHARSET=UTF-8
|
||||
LINE_BREAKER=([\r\n]+)timestamp=\"
|
||||
MAX_EVENTS=100000
|
||||
TIME_FORMAT=%s
|
||||
TIME_PREFIX=timestamp="
|
||||
TRUNCATE=0
|
||||
|
||||
# Rewrite the source Metadata to manage search time extraction
|
||||
TRANSFORMS-nmon_config_http = nmon_config_http_rewrite_host, nmon_config_http_rewrite_sourcetype
|
||||
|
||||
# For search heads
|
||||
[source::nmon_config:http]
|
||||
KV_MODE=none
|
||||
|
||||
#####################
|
||||
# nmon collect events
|
||||
#####################
|
||||
|
||||
[nmon_collect]
|
||||
SHOULD_LINEMERGE=false
|
||||
NO_BINARY_CHECK=true
|
||||
CHARSET=UTF-8
|
||||
TIME_PREFIX=^
|
||||
TIME_FORMAT=%d-%m-%Y %H:%M:%S
|
||||
MAX_TIMESTAMP_LOOKAHEAD=19
|
||||
LINE_BREAKER=([\n\r]+)\d{2}-\d{2}-\d{4}\s\d{2}:\d{2}:\d{2}
|
||||
TRUNCATE=999999
|
||||
|
||||
# Deactivate KV
|
||||
KV_MODE = none
|
||||
|
||||
###################
|
||||
# nmon clean events
|
||||
###################
|
||||
|
||||
[nmon_clean]
|
||||
SHOULD_LINEMERGE=false
|
||||
NO_BINARY_CHECK=true
|
||||
CHARSET=UTF-8
|
||||
TIME_PREFIX=^
|
||||
TIME_FORMAT=%d-%m-%Y %H:%M:%S
|
||||
MAX_TIMESTAMP_LOOKAHEAD=19
|
||||
LINE_BREAKER=([\n\r]+)\d{2}-\d{2}-\d{4}\s\d{2}:\d{2}:\d{2}
|
||||
TRUNCATE=999999
|
||||
|
||||
# Deactivate KV
|
||||
KV_MODE = none
|
||||
@ -0,0 +1,114 @@
|
||||
# transforms.conf
|
||||
|
||||
##############
|
||||
# nmon metrics
|
||||
##############
|
||||
|
||||
# host Meta overridden with 5th column
|
||||
[nmon_metrics_csv_hostoverride]
|
||||
DEST_KEY = MetaData:Host
|
||||
REGEX = ^\d*,\"{0,1}[^\"\,]*\"{0,1},\"{0,1}[^\"\,]*\"{0,1}[^\"\,]*\"{0,1},\"{0,1}[^\"\,]*\"{0,1},\"{0,1}([^\"\,]*)\"{0,1}
|
||||
FORMAT = host::$1
|
||||
|
||||
# Metrics sent over http - host
|
||||
[nmon_metrics_http_host]
|
||||
DEST_KEY = MetaData:Host
|
||||
REGEX = hostname=\"([^\"]*)\"
|
||||
FORMAT = host::$1
|
||||
|
||||
# Metrics sent over http - metric_name
|
||||
[nmon_metrics_http_metric_name]
|
||||
REGEX = metric_name=\"([^\"]*)\"
|
||||
FORMAT = metric_name::$1
|
||||
WRITE_META = true
|
||||
|
||||
[nmon_metrics_http_metric_value]
|
||||
REGEX = _value=\"([\d|\.]*)\"
|
||||
FORMAT = _value::$1
|
||||
WRITE_META = true
|
||||
|
||||
# Metrics sent over http - dimensions
|
||||
[nmon_metrics_http_dims]
|
||||
REGEX = (dimension\_\w*)=\"([^\"]*)\"
|
||||
FORMAT = $1::$2
|
||||
WRITE_META = true
|
||||
REPEAT_MATCH = true
|
||||
|
||||
# Metrics sent over http - OStype
|
||||
[nmon_metrics_http_OStype]
|
||||
REGEX = OStype=\"([^\"]*)\"
|
||||
FORMAT = OStype::$1
|
||||
WRITE_META = true
|
||||
|
||||
# Metrics sent over http - serialnum
|
||||
[nmon_metrics_http_serialnum]
|
||||
REGEX = serialnum=\"([^\"]*)\"
|
||||
FORMAT = serialnum::$1
|
||||
WRITE_META = true
|
||||
|
||||
###########
|
||||
# nmon data
|
||||
###########
|
||||
|
||||
# Host override based on event data form nmon_data sourcetype
|
||||
|
||||
[nmon_data_hostoverride]
|
||||
DEST_KEY = MetaData:Host
|
||||
REGEX = ^\"{0,1}[a-zA-Z0-9\_]+\"{0,1},\"{0,1}[a-zA-Z0-9\-\_\.]+\"{0,1},\"{0,1}([a-zA-Z0-9\-\_\.]+)\"{0,1},.+
|
||||
FORMAT = host::$1
|
||||
|
||||
# nmon data as events sent over http - host indexed field
|
||||
[nmon_data_http_host]
|
||||
DEST_KEY = MetaData:Host
|
||||
REGEX = hostname=\"([^\"]*)\"
|
||||
FORMAT = host::$1
|
||||
|
||||
# nmon data as events sent over http - OStype indexed field
|
||||
[nmon_data_http_OStype]
|
||||
REGEX = \sOStype=\"([^\"]*)\"
|
||||
WRITE_META = true
|
||||
FORMAT = OStype::$1
|
||||
DEFAULT_VALUE = NULL
|
||||
|
||||
# nmon data as events sent over http - type indexed field
|
||||
[nmon_data_http_type]
|
||||
REGEX = \stype=\"([^\"]*)\"
|
||||
WRITE_META = true
|
||||
FORMAT = type::$1
|
||||
DEFAULT_VALUE = NULL
|
||||
|
||||
# nmon data as events sent over http - rewrite sourcetype
|
||||
[nmon_data_http_sourcetype]
|
||||
DEST_KEY = MetaData:Sourcetype
|
||||
REGEX = .*
|
||||
FORMAT = sourcetype::nmon_data
|
||||
|
||||
#############
|
||||
# nmon config
|
||||
#############
|
||||
|
||||
# Host override based on event data form nmon_config sourcetype
|
||||
|
||||
[nmon_config_hostoverride]
|
||||
DEST_KEY = MetaData:Host
|
||||
REGEX = CONFIG\,[a-zA-Z0-9\-\:\.]+\,([a-zA-Z0-9\-\_\.]+)\,[a-zA-Z0-9\-\_\.]+
|
||||
FORMAT = host::$1
|
||||
|
||||
# nmon_config sent over http
|
||||
|
||||
[nmon_config_http_rewrite_host]
|
||||
DEST_KEY = MetaData:Host
|
||||
REGEX = host=\"{0,}([a-zA-Z0-9\-\_\.]+)\"{0,}
|
||||
FORMAT = host::$1
|
||||
|
||||
# nmon_config source
|
||||
[nmon_config_http_rewrite_source]
|
||||
DEST_KEY = MetaData:Source
|
||||
REGEX = .*
|
||||
FORMAT = source::configdata:http
|
||||
|
||||
# nmon_config sourcetype
|
||||
[nmon_config_http_rewrite_sourcetype]
|
||||
DEST_KEY = MetaData:Sourcetype
|
||||
REGEX = .*
|
||||
FORMAT = sourcetype::nmon_config
|
||||
@ -0,0 +1,7 @@
|
||||
|
||||
# Application-level permissions
|
||||
|
||||
[]
|
||||
owner = admin
|
||||
access = read : [ * ], write : [ admin ]
|
||||
export = system
|
||||
@ -0,0 +1,208 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"date": "2022-11-14T09:26:13.3459814Z",
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"app": {
|
||||
"id": 4022,
|
||||
"version": "1.1.1",
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"hash": "cd786d3b5421268f0f893be70011210ae5db6efbe077558d9219845d3b6c7b7a"
|
||||
},
|
||||
{
|
||||
"path": "license.txt",
|
||||
"hash": "7ddba183d8c539be99f03fe99499ddbc863cc688164bf3b5ea59d0d918a9e653"
|
||||
},
|
||||
{
|
||||
"path": "metadata/default.meta",
|
||||
"hash": "6b6c91fc18940aeb1580da6c06f92810beef8af632f73714070bae9e4a777af2"
|
||||
},
|
||||
{
|
||||
"path": "static/appLogo.png",
|
||||
"hash": "0736204483f4205c90d49c1f212e70d4e15c4d79aee19d43a0ab8247455118d1"
|
||||
},
|
||||
{
|
||||
"path": "static/appIcon.png",
|
||||
"hash": "e0611349e349b6cee55d123f85ed286a4ac2c0f1bbdbbedcbf230207bc2404ee"
|
||||
},
|
||||
{
|
||||
"path": "static/appIcon_2x.png",
|
||||
"hash": "5434fede7130f1bacc4d8e3ec48f2b3bd67367f55658b6d07d5b232b8f60f522"
|
||||
},
|
||||
{
|
||||
"path": "static/appIconAlt.png",
|
||||
"hash": "e0611349e349b6cee55d123f85ed286a4ac2c0f1bbdbbedcbf230207bc2404ee"
|
||||
},
|
||||
{
|
||||
"path": "static/appLogo_2x.png",
|
||||
"hash": "845f9bcdcd947b60e7c6d110f03debad96fee327b13a1bda2457788e069c350e"
|
||||
},
|
||||
{
|
||||
"path": "static/appIconAlt_2x.png",
|
||||
"hash": "5434fede7130f1bacc4d8e3ec48f2b3bd67367f55658b6d07d5b232b8f60f522"
|
||||
},
|
||||
{
|
||||
"path": "README/nmon.conf.spec",
|
||||
"hash": "27ec7f455744434f988312504fa3c9a89c0a77b2e76c6974ae187ddcd1e8f380"
|
||||
},
|
||||
{
|
||||
"path": "default/app.conf",
|
||||
"hash": "82887e56e863986c96284e5519d37873529be320e4c6571b8410f770af16ee77"
|
||||
},
|
||||
{
|
||||
"path": "default/nmon.conf",
|
||||
"hash": "85042666cc733e23c1bf4b96dad9eaaf8a90fbeff4770fd595d58bf54671fff2"
|
||||
},
|
||||
{
|
||||
"path": "default/nmonparser_config.json",
|
||||
"hash": "9db81c4534b90ef9f4dd67dfe0417c7c9bd669d95703d0dd91e6f27b4ac27c6c"
|
||||
},
|
||||
{
|
||||
"path": "default/props.conf",
|
||||
"hash": "865bce58bea6b7ae664d6b1f4d95457dfb41ab34e4588af9efa2625ac36325eb"
|
||||
},
|
||||
{
|
||||
"path": "default/inputs.conf",
|
||||
"hash": "1bfb384dff0bb2661d3ced596e2c74a780d229413463f0b29015f5f734e7cf3c"
|
||||
},
|
||||
{
|
||||
"path": "default/transforms.conf",
|
||||
"hash": "4c575ce58234455c879370ab8f69ed4ea6b9bd7901b6bceb53ac1049c9ad1b82"
|
||||
},
|
||||
{
|
||||
"path": "bin/nmonparser_hec.py",
|
||||
"hash": "c7262e35253317410cbf3b2fdb9e6822f80704a52afbd94abb3396c56b29d576"
|
||||
},
|
||||
{
|
||||
"path": "bin/sarmon_binaries_README",
|
||||
"hash": "ab79e66800d2291dd43dec243fb6df8abd0975dbc2183477656eb61ee95fe456"
|
||||
},
|
||||
{
|
||||
"path": "bin/sarmon_bin_i386.tgz",
|
||||
"hash": "d184fa41438ac42a974373abb4607b926a02ea0b9f64d6e7a15f13c0aad76062"
|
||||
},
|
||||
{
|
||||
"path": "bin/nmonparser_hec.pl",
|
||||
"hash": "1a28b0be80748d94f43283b65e71c300ef2171a2286330141f09daef1ea72ef5"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_consumer.sh",
|
||||
"hash": "c5662c4362572dc28fe89af2ff605f0f49c701434237c6f93ede62e0e515ca73"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_reader.sh",
|
||||
"hash": "0aaefacadc71ebc964f9de2ea11195d8552f24f2b578249f7337d6a1e8003bc0"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_cleaner.sh",
|
||||
"hash": "f932b0c3ef29fd07ff680e15e0a34f2ce3f0062cf8b6443a20750e324cbea544"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_helper.sh",
|
||||
"hash": "83a13788aefed93c7549cf2520323317fb16b691259d45db8c4dede9d4e2f73f"
|
||||
},
|
||||
{
|
||||
"path": "bin/linux.tgz",
|
||||
"hash": "376bc4ad7d197dca898ef06d03df631f5e8e6fe365e398bc2905d2146af6cc01"
|
||||
},
|
||||
{
|
||||
"path": "bin/README",
|
||||
"hash": "597cdad620bec4e52e0e8adc3cad99de9b3ce45da0dd18e4159e1009c976e957"
|
||||
},
|
||||
{
|
||||
"path": "bin/sarmon_bin_sparc.tgz",
|
||||
"hash": "73a2b9555ffdd62666063bbfa33520de4ccd68d4b5c63f08a365be5a5f2ea76f"
|
||||
},
|
||||
{
|
||||
"path": "bin/nmonparser.sh",
|
||||
"hash": "ade7e0ab7889500e443306483f4f3bccc7124d4612a77cf6b1cadf0d218f7abb"
|
||||
},
|
||||
{
|
||||
"path": "bin/create_agent.py",
|
||||
"hash": "ee69e9471d32d0d5fe2d97915d0064663ad726da74a774ca138d8b30f84fa2fa"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_cleaner.py",
|
||||
"hash": "f5a3478151137d206313de436c7556bbc32b1f18405f30abea016d8a9485c674"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_reader.py",
|
||||
"hash": "1af43497bbf7f4b9c2a637d5677fc0a964d33f4f4578170aa630c56cb90e8577"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_reader.pl",
|
||||
"hash": "582e2f1707f3904999c311fe46dce92ddbe46f2ff7b61eb3a2f5347c989e9f62"
|
||||
},
|
||||
{
|
||||
"path": "bin/linux_binaries_README",
|
||||
"hash": "1e5003fdf5a0a4b10dbdc5db1e9bc1960520e7ecd804a20f75ee6b299c043007"
|
||||
},
|
||||
{
|
||||
"path": "bin/metricator_cleaner.pl",
|
||||
"hash": "582c9bc0a35908dcecceeab631f29ff3b4725921b3239204dfb713c5853444b7"
|
||||
},
|
||||
{
|
||||
"path": "bin/lib/aix/README",
|
||||
"hash": "5931eb43b5a106c340925b610ab2a9357d752440b0c9cb32ec3041c64a92ec3e"
|
||||
},
|
||||
{
|
||||
"path": "bin/lib/aix/Text/CSV_PP.pm",
|
||||
"hash": "05de57abb2780a6ae9788af4a56f351928d3f070d29e16ee3d0d1c8c20167317"
|
||||
},
|
||||
{
|
||||
"path": "bin/lib/aix/Text/Diff.pm",
|
||||
"hash": "a3c8d575cfcf07cd50848883103ba4606653818c76d19e2d68ff0e0734966fd8"
|
||||
},
|
||||
{
|
||||
"path": "bin/lib/aix/Text/CSV.pm",
|
||||
"hash": "4c5e21fa0718c2be432fa53f2c8440e725bfc94f0649539749559807eb133420"
|
||||
},
|
||||
{
|
||||
"path": "bin/lib/aix/Text/Diff/Config.pm",
|
||||
"hash": "09dac9cd5f3903c1224d8e901a73dca610e79e1243d246fdbca2775658476b63"
|
||||
},
|
||||
{
|
||||
"path": "bin/lib/aix/Text/Diff/Table.pm",
|
||||
"hash": "c1629847301d6cfaf766a99a64423736684d615f3c9b9cdccddf4988dee9816e"
|
||||
},
|
||||
{
|
||||
"path": "bin/nmon_external_cmd/nmon_external_snap.sh",
|
||||
"hash": "794971722e75aa462dbe47fa182617c81a197da1ed6fa89398c2d2ffacf8f7cb"
|
||||
},
|
||||
{
|
||||
"path": "bin/nmon_external_cmd/nmon_external_start.sh",
|
||||
"hash": "a396573e35d651c48e3227556e0b2f45096f2d5cc7784d96cf48f02aab3abe50"
|
||||
},
|
||||
{
|
||||
"path": "bin/pre_action_scripts/README",
|
||||
"hash": "39bfe5c6f1ecbefaa9741ccfb55e425d5320c9f75334249b068ac822095864f2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"products": [
|
||||
{
|
||||
"platform": "splunk",
|
||||
"product": "enterprise",
|
||||
"versions": [
|
||||
"7.2",
|
||||
"7.3",
|
||||
"8.0",
|
||||
"8.1",
|
||||
"8.2",
|
||||
"9.0"
|
||||
],
|
||||
"architectures": [
|
||||
"x86_64"
|
||||
],
|
||||
"operatingSystems": [
|
||||
"windows",
|
||||
"linux",
|
||||
"macos",
|
||||
"freebsd",
|
||||
"solaris",
|
||||
"aix"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in new issue