Guest User

Ganglia::Gmetric::PP + v3.1 support

a guest
Apr 14th, 2011
298
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. =head1 NAME
  2.  
  3. Ganglia::Gmetric::PP - Pure Perl emitter/parser of Ganglia gmetric monitoring
  4. packets
  5.  
  6. =head1 SYNOPSIS
  7.  
  8.     my $gmetric = Ganglia::Gmetric::PP->new(host => 'localhost', port => 8649);
  9.     $gmetric->send($type, $name, $value, $units, $slope, $tmax, $dmax);
  10.  
  11. =head1 DESCRIPTION
  12.  
  13. This module constructs Ganglia packets in the manner of the gmetric program and
  14. sends them via UDP to a gmond. Additionally it can receive and/or parse gmetric
  15. packets which is useful for constructing proxies. Though written in pure Perl
  16. with no non-core dependencies, it tries to be fast.
  17.  
  18. =cut
  19.  
  20. package Ganglia::Gmetric::PP;
  21.  
  22. our $VERSION = '1.04';
  23.  
  24. use strict;
  25. use warnings;
  26.  
  27. use base 'Exporter', 'IO::Socket::INET';
  28.  
  29. our @EXPORT_OK = qw(
  30.     GANGLIA_VALUE_STRING
  31.     GANGLIA_VALUE_UNSIGNED_CHAR
  32.     GANGLIA_VALUE_CHAR
  33.     GANGLIA_VALUE_UNSIGNED_SHORT
  34.     GANGLIA_VALUE_SHORT
  35.     GANGLIA_VALUE_UNSIGNED_INT
  36.     GANGLIA_VALUE_INT
  37.     GANGLIA_VALUE_FLOAT
  38.     GANGLIA_VALUE_DOUBLE
  39.     GANGLIA_SLOPE_ZERO
  40.     GANGLIA_SLOPE_POSITIVE
  41.     GANGLIA_SLOPE_NEGATIVE
  42.     GANGLIA_SLOPE_BOTH
  43.     GANGLIA_SLOPE_UNSPECIFIED
  44.     METRIC_INDEX_TYPE
  45.     METRIC_INDEX_NAME
  46.     METRIC_INDEX_VALUE
  47.     METRIC_INDEX_UNITS
  48.     METRIC_INDEX_SLOPE
  49.     METRIC_INDEX_TMAX
  50.     METRIC_INDEX_DMAX
  51. );
  52. our %EXPORT_TAGS = (
  53.     'all' => \@EXPORT_OK,
  54. );
  55.  
  56. our $hostname;
  57.  
  58. =head1 FUNCTIONS
  59.  
  60. =over 4
  61.  
  62. =item * $gmetric = Ganglia::Gmetric::PP->new(host => $host, port => $port)
  63.  
  64. Constructs a new object which sends gmetric packets to the specified C<host>
  65. and UDP C<port>. If omitted, they default to localhost and 8649, respectively.
  66.  
  67. =item * $gmond = Ganglia::Gmetric::PP->new(listen_host => $host, listen_port => $port)
  68.  
  69. Constructs a new object which receives gmetric packets (e.g. in a gmond replacement).
  70. If the $gmetric will be used for receiving packets, C<listen_host> and
  71. C<listen_port> may be specified as well.
  72.  
  73. =cut
  74.  
  75. sub new {
  76.     my $proto = shift;
  77.     my $class = ref($proto) || $proto;
  78.  
  79.     my %p = @_;
  80.  
  81.     my %opts;
  82.  
  83.     $opts{LocalHost} = $p{listen_host} if $p{listen_host};
  84.     $opts{LocalPort} = $p{listen_port} if $p{listen_port};
  85.  
  86.     unless (%opts) {
  87.         $opts{PeerHost} = $p{host} || 'localhost';
  88.         $opts{PeerPort} = $p{port} || 8649;
  89.     }
  90.  
  91.     my $self = IO::Socket::INET->new(
  92.         Proto    => 'udp',
  93.         %opts,
  94.     );
  95.  
  96.     die "failed to create socket: $!" unless $self;
  97.  
  98.     return bless $self, $class;
  99. }
  100.  
  101. =item * $gmetric->send($type, $name, $value, $units, $slope, $tmax, $dmax)
  102.  
  103. Sends a Ganglia message. The parameters are:
  104.  
  105. =over 4
  106.  
  107. =item * $type
  108.  
  109. The type of data being sent. Must be one of these importable constants:
  110.  
  111. =over 4
  112.  
  113. =item * GANGLIA_VALUE_STRING
  114.  
  115. =item * GANGLIA_VALUE_UNSIGNED_CHAR
  116.  
  117. =item * GANGLIA_VALUE_CHAR
  118.  
  119. =item * GANGLIA_VALUE_UNSIGNED_SHORT
  120.  
  121. =item * GANGLIA_VALUE_SHORT
  122.  
  123. =item * GANGLIA_VALUE_UNSIGNED_INT
  124.  
  125. =item * GANGLIA_VALUE_INT
  126.  
  127. =item * GANGLIA_VALUE_FLOAT
  128.  
  129. =item * GANGLIA_VALUE_DOUBLE
  130.  
  131. =back
  132.  
  133. =item * $name
  134.  
  135. The name of the metric.
  136.  
  137. =item * $value
  138.  
  139. The current value of the metric.
  140.  
  141. =item * $units
  142.  
  143. A string describing the units of measure for the metric.
  144.  
  145. =item * $slope
  146.  
  147. A description of how the metric tends to change over time. Must be one of these importable constants:
  148.  
  149. =over 4
  150.  
  151. =item * GANGLIA_SLOPE_ZERO
  152.  
  153. Data is fixed, mostly unchanging.
  154.  
  155. =item * GANGLIA_SLOPE_POSITIVE
  156.  
  157. Value is always increasing (counter).
  158.  
  159. =item * GANGLIA_SLOPE_NEGATIVE
  160.  
  161. Value is always decreasing.
  162.  
  163. =item * GANGLIA_SLOPE_BOTH
  164.  
  165. Value can be anything.
  166.  
  167. =back
  168.  
  169. =item * $tmax
  170.  
  171. The maximum time in seconds between gmetric calls.
  172.  
  173. =item * $dmax
  174.  
  175. The lifetime in seconds of this metric.
  176.  
  177. =back
  178.  
  179. =cut
  180.  
  181. # exported constants. see http://code.google.com/p/embeddedgmetric/wiki/GmetricProtocol
  182. use constant {
  183.     GANGLIA_VALUE_STRING            => 'string',
  184.     GANGLIA_VALUE_UNSIGNED_CHAR     => 'uint8',
  185.     GANGLIA_VALUE_CHAR              => 'int8',
  186.     GANGLIA_VALUE_UNSIGNED_SHORT    => 'uint16',
  187.     GANGLIA_VALUE_SHORT             => 'int16',
  188.     GANGLIA_VALUE_UNSIGNED_INT      => 'uint32',
  189.     GANGLIA_VALUE_INT               => 'int32',
  190.     GANGLIA_VALUE_FLOAT             => 'float',
  191.     GANGLIA_VALUE_DOUBLE            => 'double',
  192.  
  193.     GANGLIA_SLOPE_ZERO              => 0, # data is fixed, mostly unchanging
  194.     GANGLIA_SLOPE_POSITIVE          => 1, # is always increasing (counter)
  195.     GANGLIA_SLOPE_NEGATIVE          => 2, # is always decreasing
  196.     GANGLIA_SLOPE_BOTH              => 3, # can be anything
  197.     GANGLIA_SLOPE_UNSPECIFIED       => 4,
  198.  
  199.     METRIC_INDEX_TYPE               => 0,
  200.     METRIC_INDEX_NAME               => 1,
  201.     METRIC_INDEX_VALUE              => 2,
  202.     METRIC_INDEX_UNITS              => 3,
  203.     METRIC_INDEX_SLOPE              => 4,
  204.     METRIC_INDEX_TMAX               => 5,
  205.     METRIC_INDEX_DMAX               => 6,
  206. };
  207.  
  208. # internal constants
  209. use constant {
  210.     MAGIC_ID_V25                    => 0,
  211.     GMETRIC_FORMAT_V25              => 'N(N/a*x![4])4N3',
  212.     GMETRIC_FORMAT_V31_METADATA     => 'N(N/a*x![4])2N(N/a*x![4])3N3N(N/a*x![4])*',
  213.     GMETRIC_FORMAT_V31_METADATA_U   => 'N(N/a*x![4])2N(N/a*x![4])3N3N/(N/a*x![4])',
  214.     GMETRIC_FORMAT_V31_VALUE_STRING => 'N(N/a*x![4])2N(N/a*x![4])2',
  215.     GMETRIC_FORMAT_V31_VALUE_INT32  => 'N(N/a*x![4])2N(N/a*x![4])l>',
  216.     GMETRIC_FORMAT_V31_VALUE_UINT32 => 'N(N/a*x![4])2N(N/a*x![4])N',
  217.     GMETRIC_FORMAT_V31_VALUE_FLOAT  => 'N(N/a*x![4])2N(N/a*x![4])f>',
  218.     GMETRIC_FORMAT_V31_VALUE_DOUBLE => 'N(N/a*x![4])2N(N/a*x![4])d>',
  219.  
  220.     DEFAULT_UNITS                   => '',
  221.     DEFAULT_SLOPE                   => 3,
  222.     DEFAULT_TMAX                    => 60,
  223.     DEFAULT_DMAX                    => 0,
  224.  
  225.     gmetadata_full          => 128,
  226.     gmetric_ushort          => 128 + 1,
  227.     gmetric_short           => 128 + 2,
  228.     gmetric_int             => 128 + 3,
  229.     gmetric_uint            => 128 + 4,
  230.     gmetric_string          => 128 + 5,
  231.     gmetric_float           => 128 + 6,
  232.     gmetric_double          => 128 + 7,
  233.     gmetadata_request           => 128 + 8,
  234. };
  235.  
  236. sub send_v25 {
  237.     my $self = shift;
  238.     my @msg = (MAGIC_ID_V25, @_);
  239.     $msg[4] = DEFAULT_UNITS unless defined $msg[4];
  240.     $msg[5] = DEFAULT_SLOPE unless defined $msg[5];
  241.     $msg[6] = DEFAULT_TMAX  unless defined $msg[6];
  242.     $msg[7] = DEFAULT_DMAX  unless defined $msg[7];
  243.     $self->SUPER::send(pack GMETRIC_FORMAT_V25, @msg);
  244. }
  245.  
  246. sub send_v31 {
  247.     my $self = shift;
  248.  
  249.     my ($type, $name, $value, $units, $slope, $tmax, $dmax, $spoof, $heartbeat) = @_;
  250.     $units = DEFAULT_UNITS unless defined $units;
  251.     $slope = DEFAULT_SLOPE unless defined $slope;
  252.     $tmax  = DEFAULT_TMAX  unless defined $tmax;
  253.     $dmax  = DEFAULT_DMAX  unless defined $dmax;
  254.  
  255.     my $host;
  256.     my @extra_data;
  257.  
  258.     if (defined($spoof)) {
  259.     $host = $spoof;
  260.     push @extra_data, 'SPOOF_HOST', $spoof;
  261.     } else {
  262.     unless (defined($hostname)) {
  263.         require Sys::Hostname;
  264.         $hostname = Sys::Hostname::hostname();
  265.     }
  266.     $host = $hostname;
  267.     }
  268.  
  269.     if ($heartbeat) {
  270.         $name = 'heartbeat';
  271.         $type = 'uint32';
  272.         $units = '';
  273.     $tmax = $dmax = $slope = 0;
  274.     $value = '0';
  275.     push @extra_data, 'SPOOF_HEARTBEAT', 'yes';
  276.     }
  277.  
  278.     $self->SUPER::send(pack GMETRIC_FORMAT_V31_METADATA, gmetadata_full,
  279.     $host, $name, defined($spoof) ? 1 : 0, $type, $name, $units, $slope,
  280.     $tmax, $dmax, @extra_data/2, @extra_data) or return;
  281.  
  282.     $self->SUPER::send(pack GMETRIC_FORMAT_V31_VALUE_STRING, gmetric_string,
  283.     $host, $name, defined($spoof) ? 1 : 0, '%s', $value);
  284.  
  285. }
  286.  
  287. sub send {
  288.     my $self = shift;
  289.     return  ${*$self}{proto_v25} ? $self->send_v25(@_) : $self->send_v31(@_);
  290. }
  291.  
  292. =item * @metric = $gmetric->receive()
  293.  
  294. Waits for a single gmetric packet on the UDP listen port and returns the parsed
  295. metric (see C<parse>).
  296.  
  297. =cut
  298.  
  299. sub receive {
  300.     my $self = shift;
  301.     return() unless $self->recv(my $buf, 1 << 14);
  302.     return $self->parse($buf);
  303. }
  304.  
  305. =item * @metric = Ganglia::Gmetric::PP->parse($packet_data)
  306.  
  307. Parses a gmetric packet, which is typically received by a UDP server.
  308.  
  309. The elements returned match the arguments to C<send>:
  310.  
  311.     ($type, $name, $value, $units, $slope, $tmax, $dmax) = @metric;
  312.  
  313. This function may die if the given data does not resemble a gmetric packet.
  314.  
  315. =cut
  316.  
  317. sub parse_v25 {
  318.     my @res = unpack GMETRIC_FORMAT_V25, $_[1];
  319.     die "bad magic" unless shift(@res) == MAGIC_ID_V25;
  320.     return @res;
  321. }
  322.  
  323. sub parse_v31_metadata {
  324.     my ($id, $host, $name, $spoof, $type, $name2, $units, $slope,
  325.         $tmax, $dmax, @extra_data) =
  326.     unpack GMETRIC_FORMAT_V31_METADATA_U, $_[1];
  327.  
  328.     return unless defined($name);
  329.  
  330.     return {
  331.     'host'      => $host,
  332.     'name'      => $name,
  333.     'spoof'     => $spoof,
  334.     'type'      => $type,
  335.     'units'     => $units,
  336.     'slope'     => $slope,
  337.     'tmax'      => $tmax,
  338.     'dmax'      => $dmax,
  339.     'extra_data'    => { @extra_data },
  340.     };
  341. }
  342.  
  343. sub parse_v31_value_string {
  344.     my @ret = unpack GMETRIC_FORMAT_V31_VALUE_STRING, $_[1];
  345.     shift @ret;
  346.     return @ret;
  347. }
  348.  
  349. sub parse_v31_value_int32 {
  350.     my @ret = unpack GMETRIC_FORMAT_V31_VALUE_INT32, $_[1];
  351.     shift @ret;
  352.     return @ret;
  353. }
  354.  
  355. sub parse_v31_value_uint32 {
  356.     my @ret = unpack GMETRIC_FORMAT_V31_VALUE_UINT32, $_[1];
  357.     shift @ret;
  358.     return @ret;
  359. }
  360.  
  361. sub parse_v31_value_float {
  362.     my @ret = unpack GMETRIC_FORMAT_V31_VALUE_FLOAT, $_[1];
  363.     shift @ret;
  364.     return @ret;
  365. }
  366.  
  367. sub parse_v31_value_double {
  368.     my @ret = unpack GMETRIC_FORMAT_V31_VALUE_DOUBLE, $_[1];
  369.     shift @ret;
  370.     return @ret;
  371. }
  372.  
  373.  
  374. my @parse_v31_value = (
  375.     \&parse_v31_value_uint32,
  376.     \&parse_v31_value_int32,
  377.     \&parse_v31_value_int32,
  378.     \&parse_v31_value_uint32,
  379.     \&parse_v31_value_string,
  380.     \&parse_v31_value_float,
  381.     \&parse_v31_value_double,
  382. );
  383.  
  384. sub parse {
  385.     my $self = shift;
  386.     my ($id) = unpack 'N', $_[0];
  387.  
  388.     if ($id == gmetadata_full) {
  389.     my $ret = $self->parse_v31_metadata(@_);
  390.  
  391.     ${*$self}{metadata_cache}{$ret->{'host'}}{$ret->{'name'}} = $ret
  392.         if $ret;
  393.  
  394.     return ();
  395.     } elsif ($id >= gmetric_ushort && $id <= gmetric_double) {
  396.     my $handler = $parse_v31_value[$id - gmetric_ushort];
  397.     my ($host, $name, $spoof, $fmt, $value) = $handler->($self, @_);
  398.     my $metadata = ${*$self}{metadata_cache}{$host}{$name} or
  399.         return ();
  400.     return ($metadata->{'type'}, $name, $value, $metadata->{'units'},
  401.         $metadata->{'slope'}, $metadata->{'tmax'}, $metadata->{'dmax'},
  402.         $host, $spoof, $metadata->{'extra_data'});
  403.     }
  404.  
  405.     return $self->parse_v25(@_);
  406. }
  407.  
  408. 1;
  409.  
  410. =back
  411.  
  412. =head1 AUTHOR
  413.  
  414. Adam Thomason, E<lt>athomason@cpan.org<gt>
  415.  
  416. =head1 COPYRIGHT AND LICENSE
  417.  
  418. Copyright (C) 2007-2009 by Six Apart, E<lt>cpan@sixapart.comE<gt>
  419.  
  420. This library is free software; you can redistribute it and/or modify
  421. it under the same terms as Perl itself, either Perl version 5.8.6 or,
  422. at your option, any later version of Perl 5 you may have available.
  423.  
  424. =cut
RAW Paste Data