Group
Extension

MonitisMonitorManager/lib/MonitisMonitorManager.pm

package MonitisMonitorManager;

use 5.008008;
require XML::Simple;
require Monitis;
require Thread;
require URI;
use Thread qw(async);

use strict;
no strict "refs";
use warnings;
use threads::shared;
use URI::Escape;
use MonitisMonitorManager::MonitisConnection;
use MonitisMonitorManager::M3Logger;
use Carp;
use Date::Manip;
use File::Basename;
use JSON;
use Data::Dumper;

#################
### CONSTANTS ###
#################

require Exporter;

our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use MonitisMonitorManager ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
	
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
	
);

our $VERSION = '3.12';

# use the same constant as in the Perl-SDK
use constant DEBUG => $ENV{MONITIS_DEBUG} || 0;

# constants for HTTP statistics
use constant {
	EXECUTION_PLUGIN_DIR => "Execution",
	PARSING_PLUGIN_DIR => "Parsing",
	COMPUTE_PLUGIN_DIR => "Compute",
};

our %monitis_datatypes = ( 'boolean', 1, 'integer', 2, 'string', 3, 'float', 4 );

# a helper variable to signal threads to quit on time
my $condition_loop_stop :shared = 0;

#####################
### CTOR AND DTOR ###
#####################

# constructor
sub new {
	my $class = shift;
	my $self = {@_};
	bless $self, $class;

	# initialize a static variable
	$self->{monitis_datatypes} = \%monitis_datatypes;

	# initialize M3Logger
	$self->{m3logger} = MonitisMonitorManager::M3Logger->instance();
	$self->{m3logger}->set_syslog_logging($self->is("syslog"));
	$self->{m3logger}->log_message ("debug", "M3 starting");

	# open the given XML file
	open (FILE, $self->{configuration_xml} || croak "Failed to open configuration XML: $!");
	my $templated_xml = "";
	while (<FILE>) { $templated_xml .= $_; }

	# run the macros which would change all the %SOMETHING% to a proper value
	run_macros($templated_xml);

	# load execution plugins
	$self->load_plugins_in_directory(
		plugin_table_name => "execution_plugins",
		plugin_directory => EXECUTION_PLUGIN_DIR);

	# load parsing plugins
	$self->load_plugins_in_directory(
		plugin_table_name => "parsing_plugins",
		plugin_directory => PARSING_PLUGIN_DIR);

	# load compute plugins
	$self->load_plugins_in_directory(
		plugin_table_name => "compute_plugins",
		plugin_directory => COMPUTE_PLUGIN_DIR);

	my $xml_parser = XML::Simple->new(ForceArray => 1);
	$self->{config_xml} = $xml_parser->XMLin($templated_xml);

	# a shortcut to the agents structure
	$self->{agents} = $self->{config_xml}->{agent};

	# initialize MonitisConnection - async class for Monitis interaction
	$self->{monitis_connection} = MonitisMonitorManager::MonitisConnection->new(
		apikey => "$self->{config_xml}->{apicredentials}[0]->{apikey}",
		secretkey => "$self->{config_xml}->{apicredentials}[0]->{secretkey}",
	);

	# automatically add monitors
	$self->add_agents();

	return $self;
}

# destructor
sub DESTROY {
	my $self = shift;

	# call parent dtor (not that there is any, but just to make it clean)
	$self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
	# umn, why would destroy be called multiple times?
	# because we pass $self to every running thread, so shallow copying
	# will occur. only the main thread will have MonitisConnection defined
	# though
	defined($self->{monitis_connection}) and $self->{monitis_connection}->stop();
}

##########################
### CORE FUNCTIONALITY ###
##########################

# add a single monitor
sub add_monitor {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	my $monitor_name = $args->{monitor_name};

	my $monitor_xml_path = $self->{agents}->{$agent_name}->{monitor}->{$monitor_name};

	# get the monitor tag
	my $monitor_tag = $self->get_monitor_tag(
		agent_name => $agent_name,
		monitor_name => $monitor_name);
	my $result_params = "";
	my $additional_result_params = "";
	foreach my $metric_name (keys %{$monitor_xml_path->{metric}}) {
		if ($self->metric_name_not_reserved($metric_name)) {
			my $uom = $monitor_xml_path->{metric}->{$metric_name}->{uom}[0];
			my $metric_type = $monitor_xml_path->{metric}->{$metric_name}->{type}[0];
			my $data_type = ${ $self->{monitis_datatypes} }{$metric_type} or croak "Incorrect data type '$metric_type'";
			if (defined($monitor_xml_path->{metric}->{$metric_name}->{additional})) {
				# it's an additional parameter
				$additional_result_params .= "$metric_name:$metric_name:$uom:$data_type;";
			} else {
				$result_params .= "$metric_name:$metric_name:$uom:$data_type;";
			}
		}
	}

	# monitor type (can also be undef)
	my $monitor_type = $self->{agents}->{$agent_name}->{monitor}->{$monitor_name}->{type};

	# we'll let external execution plugins dictate if they want to expose
	# additional counters (HTTP statistics for instance)
	# find the relevant execution plugin and execute its additional counters
	# function
	foreach my $execution_plugin (keys %{$self->{execution_plugins}} ) {
		if (defined($monitor_xml_path->{$execution_plugin}[0])) {
			foreach my $execution_xml_base (@{$monitor_xml_path->{$execution_plugin}}) {
				# it's called a URI since it can be anything, from a command line
				# executable, URL, SQL command...
				$self->{m3logger}->log_message ("debug", "Calling extra_counters_cb for plugin: '$execution_plugin', monitor_name->'$monitor_name'");
				# executable, URL, SQL command...
				$result_params .= $self->{execution_plugins}{$execution_plugin}->extra_counters_cb($self->{monitis_datatypes}, $execution_xml_base);
			}
		}
	}

	# remove redundant last ';'
	$result_params =~ s/;$//;

	# a simple sanity check
	if ($result_params eq "") {
		$self->{m3logger}->log_message ("info", "ResultParams are empty for monitor '$monitor_name'... Skipping!");
		return;
	}

	$self->{m3logger}->log_message ("debug", "Adding monitor '$monitor_name' with metrics '$result_params'");

	# call Monitis using the api context provided
	if ($self->is("dry_run")) {
		# don't output this line if just testing configuration
		not $self->is("test_config") and $self->{m3logger}->log_message ("info", "This is a dry run, the monitor '$monitor_name' was not really added.");
	} else {
		my @add_monitor_optional_params;
		defined($monitor_type) && push @add_monitor_optional_params, type => $monitor_type;
		if($additional_result_params ne "") { push @add_monitor_optional_params, additionalResultParams => $additional_result_params; }
		$self->add_monitor_raw(
			monitor_name => $monitor_name,
			monitor_tag =>  $monitor_tag,
			result_params =>  $result_params,
			optional_params =>  \@add_monitor_optional_params);
	}
}

# add all monitors for all agents
sub add_agents {
	my $self = shift;

	# iterate on agents and add them one by one
	foreach my $agent_name (keys %{$self->{agents}}) {
		$self->{m3logger}->log_message ("debug", "Adding agent '$agent_name'");
		$self->add_agent_monitors(agent_name => $agent_name);
	}
}

# add one agent
sub add_agent_monitors {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	
	# iterate on all monitors and add them
	foreach my $monitor_name (keys %{$self->{agents}->{$agent_name}->{monitor}} ) {
		$self->{m3logger}->log_message ("debug", "Adding monitor '$monitor_name' for agent '$agent_name'");
		$self->add_monitor(
			agent_name => $agent_name,
			monitor_name =>  $monitor_name);
	}
}

# invoke a single monitor
sub invoke_monitor {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	my $monitor_name = $args->{monitor_name};

	# get the xml path for that monitor
	my $monitor_xml_path = $self->{agents}->{$agent_name}->{monitor}->{$monitor_name};

	my $output = "";
	my $execution_called = undef;

	# result set hash
	my %results = ();
	my %additional_results = ();

	# if just testing monitors - print a nice message
	($self->is("test_config")) and $self->{m3logger}->log_message ("info", "Testing monitor '$monitor_name': ");
	my $config_ok = 1;

	# find the relevant execution plugin and execute it
	# TODO execution might be out of order in some cases
	foreach my $execution_plugin (keys %{$self->{execution_plugins}} ) {
		if (defined($monitor_xml_path->{$execution_plugin}[0])) {
			foreach my $execution_xml_base (@{$monitor_xml_path->{$execution_plugin}}) {
				# it's called a URI since it can be anything, from a command line
				# executable, URL, SQL command...
				$self->{m3logger}->log_message ("debug", "Calling execution plugin: '$execution_plugin', execution_xml_base->'$execution_xml_base', monitor_name->'$monitor_name'");
				my %returned_results = ();
				if ($self->is("test_config")) {
					my %tmp_hash = ();
					eval {
						$self->{execution_plugins}{$execution_plugin}->get_config($execution_xml_base, \%tmp_hash);
					};
					if ($@) {
						$self->{m3logger}->log_message ("err", "Configuration error: $@");
						$config_ok = 0;
					}
				} else {
					$output .= $self->{execution_plugins}{$execution_plugin}->execute($execution_xml_base, \%returned_results, $monitor_name);
					$output .= "\n";

					# merge the returned results into the main %results hash
					@results{keys %returned_results} = values %returned_results;

					# we will not break execution as we might execute a few plugins
					$execution_called = 1;
				}
			}
		}
	}

	# just testing configuration? - alright, quit!
	if ($self->is("test_config")) {
		($config_ok == 1) and $self->{m3logger}->log_message ("info", "Monitor '$monitor_name' -> Configuration is OK");
		return;
	}

	# did we call anything at all??
	if (!defined($execution_called)) {
		croak "Could not find proper execution plugin for monitor '$monitor_name'";
	}

	my $retval = 1;
	# if mass load is set, we'll handle the lines one by one
	if ($self->is("mass_load")) {
		foreach my $line (split /[\r\n]+/, $output) {
			$retval = $self->handle_output_chunk(
				agent_name => $agent_name,
				monitor_xml_path => $monitor_xml_path,
				monitor_name => $monitor_name,
				ref_results => \%results,
				ref_additional_results => \%additional_results,
				output => $line);
		}
	} else {
		$retval = $self->handle_output_chunk(
			agent_name => $agent_name,
			monitor_xml_path => $monitor_xml_path,
			monitor_name => $monitor_name,
			ref_results => \%results,
			ref_additional_results => \%additional_results,
			output => $output);
	}
}

# handle a chunk of output after executing plugins
# basically we parse stuff here...
sub handle_output_chunk {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	my $monitor_name = $args->{monitor_name};
	my $monitor_xml_path = $args->{monitor_xml_path};
	my $output = $args->{output};
	my %results = %{$args->{ref_results}};
	my %additional_results = %{$args->{ref_additional_results}};

	foreach my $metric_name (keys %{$monitor_xml_path->{metric}} ) {
		# call the relevant parsing plugin
		my %returned_results = ();

		# run the parsing plugin one by one
		foreach my $potential_parsing_plugin (keys %{$monitor_xml_path->{metric}->{$metric_name}}) {
			if (defined($self->{parsing_plugins}{$potential_parsing_plugin})) {
				$self->{m3logger}->log_message ("debug", "Calling parsing plugin: '$potential_parsing_plugin'");
				$self->{parsing_plugins}{$potential_parsing_plugin}->parse($metric_name, $monitor_xml_path->{metric}->{$metric_name}, $output, \%returned_results);
			}
		}

		# call the relevant compute plugins
		# TODO computation might be out of order in some cases when chaining
		foreach my $potential_compute_plugin (keys %{$monitor_xml_path->{metric}->{$metric_name}}) {
			if (defined($self->{compute_plugins}{$potential_compute_plugin})) {
				foreach my $code (@{$monitor_xml_path->{metric}->{$metric_name}->{$potential_compute_plugin}}) {
					$self->{m3logger}->log_message ("debug", "Calling compute plugin: '$potential_compute_plugin'");
					my $code = $monitor_xml_path->{metric}->{$metric_name}->{$potential_compute_plugin}[0];
					$self->{compute_plugins}{$potential_compute_plugin}->compute($agent_name, $monitor_name, $monitor_xml_path, $code, \%returned_results);
				}
			}
		}

		# merge the returned results into the main %results hash
		if (defined($monitor_xml_path->{metric}->{$metric_name}->{additional})) {
			@additional_results{keys %returned_results} = values %returned_results;
		} else {
			@results{keys %returned_results} = values %returned_results;
		}
	}

	# TODO 'MONITIS_CHECK_TIME' hardcoded
	# if MONITIS_CHECK_TIME is defined, use it as the timestamp for updating data
	# note: @checktime can be empty, then we'll calculate the current timestamp
	my @checktime;
	if (defined($results{MONITIS_CHECK_TIME})) {
		if (int($results{MONITIS_CHECK_TIME}) == $results{MONITIS_CHECK_TIME}) {
			# no need for date manipulation
			push @checktime, "checktime" => $results{MONITIS_CHECK_TIME};
		} else {
			my $date = new Date::Manip::Date;
			$date->parse($results{MONITIS_CHECK_TIME});
			# checktime here is seconds, update_data_for_monitor will multiply by
			# 1000 to make it milliseconds
			push @checktime, "checktime" => $date->secs_since_1970_GMT();
		}
		# and remove it from the hash
		delete $results{MONITIS_CHECK_TIME};
	}

	# format results
	my $formatted_results = format_results(\%results);
	my $formatted_additional_results = format_results_json(\%additional_results);

	return $self->update_data_for_monitor(
		agent_name => $agent_name,
		monitor_name => $monitor_name,
		results => $formatted_results,
		additional_results => $formatted_additional_results,
		@checktime);
}

# update data for a monitor, calling Monitis API
sub update_data_for_monitor {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	my $monitor_name = $args->{monitor_name};
	my $results = $args->{results};
	my $additional_results = $args->{additional_results};

	# did the user specify a checktime?
	my $checktime;
	if (defined($args->{checktime})) {
		$checktime = $args->{checktime} * 1000;
	} else {
		# get the time now (time returns time in seconds, multiply by 1000
		# for miliseconds)
		$checktime = time * 1000;
	}

	# sanity check of results...
	if ($results eq "") {
		$self->{m3logger}->log_message ("info", "Result set is empty! did it parse well? - Will not update any data!"); 
		return;
	}

	if ($self->is("dry_run")) {
		$self->{m3logger}->log_message ("info", "OK");
		$self->{m3logger}->log_message ("info", "This is a dry run, data for monitor '$monitor_name' was not really updated.");
		return;
	}

	# queue it on MonitisConnection which will handle the rest
	my $monitor_tag = $self->get_monitor_tag(
		agent_name => $agent_name,
		monitor_name => $monitor_name);
	$self->update_data_for_monitor_raw(
		agent_name => $agent_name,
		monitor_name => $monitor_name,
		monitor_tag => $monitor_tag,
		checktime => $checktime,
		results => $results,
		additional_results => $additional_results);
}

# invoke all agents, one by one
sub invoke_agents {
	my $self = shift;
	foreach my $agent_name (keys %{$self->{agents}} ) {
		$self->invoke_agent_monitors(agent_name => $agent_name);
	}
}

# invoke all monitors, one by one
sub invoke_agent_monitors {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	foreach my $monitor_name (keys %{$self->{agents}->{$agent_name}->{monitor}}) {
		$self->invoke_monitor(
			agent_name => $agent_name,
			monitor_name => $monitor_name);
	}
}

# signals threads to stop execution
sub agents_loop_stop {
	MonitisMonitorManager::M3Logger->instance()->log_message ("info", "Stopping execution...");
	lock($condition_loop_stop);
	$condition_loop_stop = 1;
	cond_broadcast($condition_loop_stop);
}

# invoke all agents in a loop with timers enabled
sub invoke_agents_loop {
	my $self = shift;
	# initialize all the agents
	my @threads = ();

	foreach my $agent_name (keys %{$self->{agents}} ) {
		push @threads, threads->create(\&invoke_agent_monitors_loop, $self, agent_name => $agent_name);
	}
	my $running_threads = @threads;

	# register SIGINT to stop the loop
	local $SIG{'INT'} = \&MonitisMonitorManager::agents_loop_stop;

	do {
		foreach my $thread (@threads) {
			if($thread->is_joinable()) {
				$thread->join();
				$self->{m3logger}->log_message ("debug", "Thread '$thread' has quitted.");
				$running_threads--;
			}
		}
		sleep 1;
	} while($running_threads > 0);
}

# invoke all monitors of an agent in a loop, taking care to sleep between
# executions
sub invoke_agent_monitors_loop {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	my $agent_interval = $self->{agents}->{$agent_name}->{interval};
	$self->{m3logger}->log_message ("debug", "Agent '$agent_name' will be invoked every '$agent_interval' seconds'");

	# this loop will break when the user will hit ^C (SIGINT)
	do {
		foreach my $monitor_name (keys %{$self->{agents}->{$agent_name}->{monitor}}) {
			$self->invoke_monitor(
				agent_name => $agent_name,
				monitor_name => $monitor_name);
		}
		lock($condition_loop_stop);
		cond_timedwait($condition_loop_stop, time() + $agent_interval);
	} while(not $condition_loop_stop);
}

#####################
### RAW FUNCTIONS ###
#####################

# handles a raw command (add_monitor, update_data)
sub handle_raw_command {
	my $self = shift;
	my ($args) = {@_};
	my $raw_command = $args->{raw_command};

	$self->{m3logger}->log_message ("debug", "Raw command is: '$raw_command'");
	my (@raw_parameters) = split /\s+/, $raw_command;
	my $command = shift @raw_parameters;

	# a quick debug message
	$self->{m3logger}->log_message ("debug", "Handling raw command: '$command'");

	for ($command) {
		/add_monitor/ and do {
			my $monitor_name = shift @raw_parameters;
			my $monitor_tag = shift @raw_parameters;
			my $result_params = shift @raw_parameters;
			my $additional_result_params = shift @raw_parameters;
			my @optional_parameters;
			if($additional_result_params ne "") { push @optional_parameters, additionalResultParams => $additional_result_params; }
			$self->add_monitor_raw(
				monitor_name => $monitor_name,
				monitor_tag => $monitor_tag,
				result_params =>  $result_params,
				optional_params =>  \@optional_parameters);
		};
		/update_data/ and do {
			my $monitor_name = shift @raw_parameters;
			my $monitor_tag = shift @raw_parameters;
			my $results = shift @raw_parameters;
			my $additional_results = shift @raw_parameters;
			$self->update_data_for_monitor_raw(
				monitor_name => $monitor_name,
				monitor_tag => $monitor_tag,
				checktime => time * 1000,
				results => $results,
				additional_results => $additional_results);
		};
		/list_monitors/ and do {
			$self->list_monitors_raw();
		};
		/delete_monitor/ and do {
			my $monitor_id = shift @raw_parameters;
			$self->delete_monitor_raw(monitor_id => $monitor_id);
		};
	}
}

# updated raw data for monitor
sub add_monitor_raw {
	my $self = shift;

	# simply forward the parameters!
	$self->{monitis_connection}->add_monitor(@_);
}

# list monitors
sub list_monitors_raw {
	my $self = shift;
	my @monitors = $self->{monitis_connection}->list_monitors();
	my $i = 0;

	printf("ID   |Name           |Tag                      |Type           |\n");
	printf("-----|---------------|-------------------------|---------------|\n");
	while (defined($monitors[0][$i])) {
		my ($monitor_name) = $monitors[0][$i]->{name};
		my ($monitor_type) = $monitors[0][$i]->{type};
		my ($monitor_tag) = $monitors[0][$i]->{tag};
		my ($monitor_id) = $monitors[0][$i]->{id};
		printf("%-5s|%-15s|%-25s|%-15s|\n", $monitor_id, $monitor_name, $monitor_tag, $monitor_type);
		$i++;
	}
}

# delete a monitor
sub delete_monitor_raw {
	my $self = shift;
	$self->{monitis_connection}->delete_monitor(@_);
}

# update data for a monitor, the internal function
sub update_data_for_monitor_raw {
	my $self = shift;
	$self->{monitis_connection}->queue(@_);
}


###############################
### SMALL UTILITY FUNCTIONS ###
###############################

# a simple function to dynamically load all perl packages in a given
# directory
sub load_plugins_in_directory {
	my $self = shift;
	my ($args) = {@_};	
	my $plugin_table_name = $args->{plugin_table_name};
	my $plugin_directory = $args->{plugin_directory};

	# initialize a new plugin table
	$self->{$plugin_table_name} = ();

	# TODO a little ugly - but this is how we're going to discover where M3
	# was installed...
	my $m3_perl_module_directory = dirname($INC{"MonitisMonitorManager.pm"}) . "/MonitisMonitorManager";
	my $full_plugin_directory = $m3_perl_module_directory . "/" . $plugin_directory;
	# iterate on all plugins in directory and load them
	foreach my $plugin_file (<$full_plugin_directory/*.pm>) {
		my $plugin_name = "MonitisMonitorManager::" . $plugin_directory . "::" . basename($plugin_file);
		$plugin_name =~ s/\.pm$//g;
		# load the plugin
		eval {
			require "$plugin_file";
			$plugin_name->name();
		};
		if ($@) {
			croak "error: $@";
		} else {
			$self->{m3logger}->log_message ("debug", "Loading plugin '" . $plugin_name . "'->'" . $plugin_name->name() . "'");
			$self->{$plugin_table_name}{$plugin_name->name()} = "$plugin_name";
		}
	}
}


# tests an attribute, such as 'dry_run', 'mass_load', 'test_config' etc.
sub is {
	my ($self, $attribute) = @_;
	if (defined($self->{$attribute}) and $self->{$attribute} == 1) {
		return 1;
	} else {
		return 0;
	}
}

# print the XML after it was templated
sub templated_xml {
	my $self = shift;
	my $xmlout = XML::Simple->new(RootName => 'config');
	return $xmlout->XMLout($self->{config_xml});
}

# formats a monitor tag from a name
sub get_monitor_tag {
	my $self = shift;
	my ($args) = {@_};
	my $agent_name = $args->{agent_name};
	my $monitor_name = $args->{monitor_name};

	# if monitor tag is defined, use it!
	if (defined ($self->{agents}->{$agent_name}->{monitor}->{$monitor_name}->{tag}) ) {
		my $monitor_tag = $self->{agents}->{$agent_name}->{monitor}->{$monitor_name}->{tag};
		$self->{m3logger}->log_message ("debug", "Obtained monitor tag '$monitor_tag' from XML");
		return $monitor_tag;
	} else {
		# make a monitor tag from name
		{ $_ = $monitor_name; s/ /_/g; return $_ }
	}
}

# formats the hash of results into a string
sub format_results(%) {
	my (%results) = %{$_[0]};
	my $formatted_results = "";
	foreach my $key (keys %results) {
		$formatted_results .= $key . ":" . uri_escape($results{$key}) . ";";
	}
	# remove redundant last ';'
	$formatted_results =~ s/;$//;
	return $formatted_results;
}

# formats the hash of results into a string
sub format_results_json(%) {
	my (%results) = %{$_[0]};
	return "[" . encode_json(\%results) . "]";
}

# simply replaces the %SOMETHING% with the relevant
# return of a defined function
sub run_macros() {
	$_[0] =~ s/%(\w+)%/replace_template($1)/eg;
}

# macro functions
sub replace_template($) {
	my ($template) = @_;
	my $callback = "_get_$template";
	return &$callback;
}

sub metric_name_not_reserved($$) {
	my $self = shift;
	my ($metric_name) = @_;
	return $metric_name ne "MONITIS_CHECK_TIME";
}


# Preloaded methods go here.

1;
__END__
# Below is stub documentation for your module. You'd better edit it!

=head1 NAME

Monitis Monitor Manager => MMM => M3

M3 is the shortened name for Monitis Monitor Manager.

This Perl module helps you manage Custom Monitors on Monitis (www.monitis.com).

=head1 SYNOPSIS

  use MonitisMonitorManager;

  # dry run dictates whether to upload data to Monitis - yes or no
  my $dry_run = 0;

  # configuration_xml is a file with the configuration XML, refer to some
  # examples here: https://github.com/monitisexchange/Monitis-Linux-Scripts/tree/master/M3v3/monitis-m3/usr/local/share/monitis-m3/sample_config
  my $configuration_xml = "/etc/m3.d/config.xml";

  # test_config dictates whether to just test the configuration or actually
  # do a proper run
  my $test_config = 0;

  # initialize the M3 instance
  my $M3 = MonitisMonitorManager->new(
    configuration_xml => $xmlfile,
    dry_run => $dry_run,
    test_config => $test_config);

  # handle a raw command in the form of:
  # 'add_monitor memory memory free:free:Bytes:2;active:active:Bytes:2'
  # 'update_data memory memory free:305594368;active:879394816'
  $M3->handle_raw_command($raw);

  # runs just one iteration of the agents defined in the XML
  $M3->invoke_agents();

  # invoke the agents in a loop (using the defined interval in the XML)
  $M3->invoke_agents_loop();

=head1 DESCRIPTION

For full proper documentation please refer to:
https://github.com/monitisexchange/Monitis-Linux-Scripts/blob/master/M3v3/README.md

M3 Perl module comes with an init.d service. If you're using a RPM or DEB
package then you're good to do, however the CPAN installation will not take
care of this...
Find it here:
https://github.com/monitisexchange/Monitis-Linux-Scripts/tree/master/M3v3

=head2 EXPORT

MonitisMonitorManager

=head1 SEE ALSO

See also Monitis' blog with entries about M3:
http://blog.monitis.com/index.php/tag/m3/

Monitis main website:
http://www.monitis.com

Github repository:
https://github.com/monitisexchange/Monitis-Linux-Scripts/tree/master/M3v3

=head1 AUTHOR

Dan Fruehauf, E<lt>malkodan@gmail.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2012 by Dan Fruehauf

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.14.2 or,
at your option, any later version of Perl 5 you may have available.


=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.