librato-metrics-taps 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +42 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +46 -0
- data/LICENSE.txt +20 -0
- data/README.md +145 -0
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/bin/librato-metrics-tap-jmxbeans +209 -0
- data/examples/cassandra/cfstats-0_7_6-2.yaml +18 -0
- data/examples/cassandra/cfstats-0_8_1.yaml +20 -0
- data/examples/cassandra/cfstats-0_8_8.yaml +20 -0
- data/examples/cassandra/cfstats-1_1_6.yaml +29 -0
- data/examples/cassandra/tpstats-0_7_6-2.yaml +70 -0
- data/examples/cassandra/tpstats-0_8_1.yaml +62 -0
- data/examples/cassandra/tpstats-0_8_8.yaml +82 -0
- data/examples/cassandra/tpstats-1_1_6.yaml +137 -0
- data/examples/java-lang.yaml +21 -0
- data/lib/librato-metrics-taps.rb +0 -0
- data/lib/librato/metrics/taps.rb +24 -0
- data/lib/librato/metrics/taps/jmx.rb +141 -0
- data/lib/librato/metrics/taps/publisher.rb +88 -0
- data/librato-metrics-taps.gemspec +30 -0
- data/temp_sensors/check_em01.c +657 -0
- data/temp_sensors/report-em01b.rb +90 -0
- data/test/helper.rb +18 -0
- data/test/test_librato-metrics-taps.rb +7 -0
- metadata +199 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
java.lang:type=ClassLoading:
|
2
|
+
LoadedClassCount:
|
3
|
+
TotalLoadedClassCount: counter
|
4
|
+
UnloadedClassCount:
|
5
|
+
java.lang:type=GarbageCollector,name=ConcurrentMarkSweep:
|
6
|
+
CollectionCount: counter
|
7
|
+
CollectionTime: counter
|
8
|
+
java.lang:type=GarbageCollector,name=ParNew:
|
9
|
+
CollectionCount: counter
|
10
|
+
CollectionTime: counter
|
11
|
+
java.lang:type=Memory:
|
12
|
+
HeapMemoryUsage:
|
13
|
+
NonHeapMemoryUsage:
|
14
|
+
java.lang:type=MemoryPool,name=CMS Old Gen:
|
15
|
+
CollectionUsage:
|
16
|
+
PeakUsage:
|
17
|
+
Usage:
|
18
|
+
java.lang:type=MemoryPool,name=CMS Perm Gen:
|
19
|
+
CollectionUsage:
|
20
|
+
PeakUsage:
|
21
|
+
Usage:
|
File without changes
|
@@ -0,0 +1,24 @@
|
|
1
|
+
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift __LIB_DIR__ unless
|
4
|
+
$LOAD_PATH.include?(__LIB_DIR__) ||
|
5
|
+
$LOAD_PATH.include?(File.expand_path(__LIB_DIR__))
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'jmx4r'
|
9
|
+
require 'librato/metrics'
|
10
|
+
require 'trollop'
|
11
|
+
|
12
|
+
module Librato
|
13
|
+
module Metrics
|
14
|
+
module Taps
|
15
|
+
|
16
|
+
def self.version
|
17
|
+
File.read(File.join(File.dirname(__FILE__), '../../../VERSION')).chomp
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'librato/metrics/taps/jmx.rb'
|
24
|
+
require 'librato/metrics/taps/publisher.rb'
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Librato
|
2
|
+
module Metrics
|
3
|
+
module Taps
|
4
|
+
module JMX
|
5
|
+
class << self
|
6
|
+
def connect!(host = 'localhost', port = 3000, username = nil, password = nil)
|
7
|
+
begin
|
8
|
+
::JMX::MBean.establish_connection :host => host, :port => port, :username => username, :pasword => password
|
9
|
+
rescue => err
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
@connected = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def match_beans(beans)
|
16
|
+
raise "Not connected" unless @connected || connect!
|
17
|
+
|
18
|
+
begin
|
19
|
+
b = ::JMX::MBean.find_all_by_name(beans.to_s)
|
20
|
+
rescue => err
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
|
24
|
+
b.collect { |bean| bean.object_name.to_s }
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Retrieves a list of JMX attributes converted to
|
29
|
+
# gauges and counters.
|
30
|
+
#
|
31
|
+
# Argument options:
|
32
|
+
#
|
33
|
+
# beans = ['bean1', 'bean2']
|
34
|
+
# Will retrieve each attribute of each beanname as
|
35
|
+
# a gauge with a count value of 1.
|
36
|
+
#
|
37
|
+
# beans = {'bean1' => ['attr1', 'attr2'], 'bean2' => true}
|
38
|
+
# Will retrieve only the specified attributes for each
|
39
|
+
# bean name. To retrieve all attributes, simply set
|
40
|
+
# to true instead of a list.
|
41
|
+
#
|
42
|
+
# beans = {'bean1' => {'attr' => 'gauge',
|
43
|
+
# 'attr2' => 'counter'}}
|
44
|
+
# Will retrieve the specified beans and their
|
45
|
+
# attributes like the example above, but with the
|
46
|
+
# ability to specify how each attribute is returned. By
|
47
|
+
# default, all attributes are returned as
|
48
|
+
# gauges. However, if you set the attribute name to
|
49
|
+
# 'counter', it will return the attribute as a counter.
|
50
|
+
#
|
51
|
+
#
|
52
|
+
def retrieve(bean_names, ignore_missing = false)
|
53
|
+
raise "Not connected" unless @connected || connect!
|
54
|
+
|
55
|
+
gauges = {}
|
56
|
+
counters = {}
|
57
|
+
(bean_names.keys rescue bean_names).each do |bean|
|
58
|
+
begin
|
59
|
+
b = ::JMX::MBean.find_by_name(bean.to_s)
|
60
|
+
rescue
|
61
|
+
if ignore_missing
|
62
|
+
next
|
63
|
+
else
|
64
|
+
raise "No such bean: #{bean}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if bean_names.respond_to?(:keys) &&
|
69
|
+
( bean_names[bean].class == Array ||
|
70
|
+
bean_names[bean].class == Hash)
|
71
|
+
attrs = bean_names[bean]
|
72
|
+
else
|
73
|
+
attrs = b.attributes.keys
|
74
|
+
end
|
75
|
+
|
76
|
+
attrs.each do |attr|
|
77
|
+
attrname = attr.first
|
78
|
+
begin
|
79
|
+
value = b.send(snake_case(attrname.to_s))
|
80
|
+
rescue
|
81
|
+
if ignore_missing
|
82
|
+
value = nil
|
83
|
+
else
|
84
|
+
raise "Bean #{bean} has no such attribute: #{attrname}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Skip attributes without a value
|
89
|
+
next unless value
|
90
|
+
|
91
|
+
# Take a look at the type and only handle types that we know how to handle
|
92
|
+
if value.kind_of? Java::JavaxManagementOpenmbean::CompositeDataSupport
|
93
|
+
# If this is a composit data type loop through the elements assuming they are numbers
|
94
|
+
value.each do |value_subkey, value_subvalue|
|
95
|
+
if value_subvalue.kind_of? Numeric
|
96
|
+
if attrs.respond_to?(:keys) && "#{attr.last}".downcase == 'counter'
|
97
|
+
counters[metric_name(bean, attrname + "." + value_subkey)] = value_subvalue.to_i
|
98
|
+
else
|
99
|
+
gauges[metric_name(bean, attrname + "." + value_subkey)] = value_subvalue
|
100
|
+
end
|
101
|
+
else
|
102
|
+
raise "The subvalue \"#{value_subkey}\" of value \"#{value}\" in Bean \"#{bean}\" is of type \"#{value_subvalue.class}\" and I only know how to handle numbers.\n"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
elsif value.kind_of? Numeric
|
106
|
+
# Skip bogus values
|
107
|
+
next if value.to_f.nan? || value.to_f.infinite?
|
108
|
+
|
109
|
+
# If this is a number go ahead and submit it as either a counter or gauge
|
110
|
+
# depending on what we set in the attributes
|
111
|
+
if attrs.respond_to?(:keys) && "#{attr.last}".downcase == 'counter'
|
112
|
+
counters[metric_name(bean, attrname)] = value.to_i
|
113
|
+
else
|
114
|
+
gauges[metric_name(bean, attrname)] = value
|
115
|
+
end
|
116
|
+
else
|
117
|
+
raise "The value \"#{value}\" of Bean \"#{bean}\" is of type \"#{value.class}\" and I do not know how to handle that type.\n"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
[counters, gauges]
|
123
|
+
end
|
124
|
+
|
125
|
+
# From ActiveSupport
|
126
|
+
def snake_case(camel_cased_word)
|
127
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
128
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
129
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
130
|
+
tr("-", "_").
|
131
|
+
downcase
|
132
|
+
end
|
133
|
+
|
134
|
+
def metric_name(bean_name, attr_name)
|
135
|
+
"#{bean_name.gsub('=', ':').gsub(/[, ]/, '_')}::#{attr_name}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Librato
|
2
|
+
module Metrics
|
3
|
+
module Taps
|
4
|
+
class Publisher
|
5
|
+
SOURCE_NAME_REGEX = /^[-A-Za-z0-9_.]{1,255}$/
|
6
|
+
|
7
|
+
@url = nil
|
8
|
+
@user = nil
|
9
|
+
@passwd = nil
|
10
|
+
|
11
|
+
DEFAULT_BATCH_SIZE = 300
|
12
|
+
|
13
|
+
def initialize(opts)
|
14
|
+
unless !opts[:source] || opts[:source] =~ SOURCE_NAME_REGEX
|
15
|
+
raise "Invalid source"
|
16
|
+
end
|
17
|
+
|
18
|
+
unless !opts[:prefix] || opts[:prefix] =~ SOURCE_NAME_REGEX
|
19
|
+
raise "Invalid prefix"
|
20
|
+
end
|
21
|
+
|
22
|
+
@client = Librato::Metrics::Client.new
|
23
|
+
@client.authenticate(opts[:email], opts[:token])
|
24
|
+
@client.api_endpoint = opts[:metrics_url]
|
25
|
+
@client.agent_identifier("librato-metrics-tap-jmxbeans/%s" %
|
26
|
+
[Taps.version])
|
27
|
+
|
28
|
+
qparams = {
|
29
|
+
:autosubmit_count => DEFAULT_BATCH_SIZE
|
30
|
+
}
|
31
|
+
|
32
|
+
[:prefix, :source].each do |o|
|
33
|
+
qparams[o] = opts[o] if opts[o]
|
34
|
+
end
|
35
|
+
|
36
|
+
@queue_params = qparams
|
37
|
+
end
|
38
|
+
|
39
|
+
# Post metrics to metrics service
|
40
|
+
#
|
41
|
+
# counters:
|
42
|
+
# {:my-counter-1 => 4, :my-counter-2 => 6, ...}
|
43
|
+
#
|
44
|
+
# gauges:
|
45
|
+
# {:my-gauge-1 => 4, ...}
|
46
|
+
# or
|
47
|
+
# {:my-gauge-2 => {:value => 5, :count => 4,
|
48
|
+
# :min => 3, :max => 6, ...}}
|
49
|
+
# or mix-match.
|
50
|
+
def post(counters, gauges, params = {})
|
51
|
+
mt = params[:measure_time] || now_secs
|
52
|
+
q = @client.new_queue(@queue_params)
|
53
|
+
|
54
|
+
if counters.length > 0
|
55
|
+
counters.each_pair do |k, v|
|
56
|
+
q.add k => {:type => :counter, :value => v, :measure_time => mt}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if gauges.length > 0
|
61
|
+
params[:gauges] = {}
|
62
|
+
gauges.each_pair do |k, v|
|
63
|
+
if v.respond_to?(:keys)
|
64
|
+
q.add k => v.merge(:measure_time => mt)
|
65
|
+
else
|
66
|
+
q.add k => {:value => Float(v), :measure_time => mt}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
q.submit
|
73
|
+
rescue => err
|
74
|
+
# XXX: ever get here? what about auto-submit?
|
75
|
+
puts "Failed to publish stats (unknown exception): #{err.inspect}"
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def now_secs
|
83
|
+
Time.now.tv_sec
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{librato-metrics-taps}
|
5
|
+
s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).chomp
|
6
|
+
|
7
|
+
s.authors = [%q{Librato, Inc.}]
|
8
|
+
s.date = %q{2012-05-15}
|
9
|
+
s.description = %q{Taps for extracting metrics data and publishing to Librato Metrics}
|
10
|
+
s.email = %q{mike@librato.com}
|
11
|
+
s.summary = %q{Librato Metrics Taps}
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.homepage = %q{http://github.com/librato/librato-metrics-taps}
|
19
|
+
s.licenses = [%q{MIT}]
|
20
|
+
|
21
|
+
s.add_runtime_dependency(%q<jmx4r>, ["= 0.1.4"])
|
22
|
+
s.add_runtime_dependency(%q<librato-metrics>, ["~> 1.0.4"])
|
23
|
+
s.add_runtime_dependency(%q<trollop>, ["= 1.16.2"])
|
24
|
+
s.add_runtime_dependency(%q<jruby-openssl>, ["= 0.8.5"])
|
25
|
+
|
26
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
27
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,657 @@
|
|
1
|
+
/* Print Debug Info. Uncomment the #define below to print debugging to stderr */
|
2
|
+
|
3
|
+
//#define DEBUG
|
4
|
+
|
5
|
+
/* Number of seconds to timeout */
|
6
|
+
#define CONNECT_TIMEOUT 5
|
7
|
+
#define MAX_RETRIES 5
|
8
|
+
/******************************************************************************
|
9
|
+
|
10
|
+
Esensors EM01 Plugin.
|
11
|
+
Description: This plugin is written mainly for Nagios, but can be
|
12
|
+
easily used for other software too.
|
13
|
+
|
14
|
+
This program is free software; you can redistribute it and/or modify
|
15
|
+
it under the terms of the GNU General Public License as published by
|
16
|
+
the Free Software Foundation; either version 2 of the License, or
|
17
|
+
(at your option) any later version.
|
18
|
+
|
19
|
+
This program is distributed in the hope that it will be useful,
|
20
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22
|
+
GNU General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License
|
25
|
+
along with this program; if not, write to the Free Software
|
26
|
+
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
27
|
+
|
28
|
+
Credits:
|
29
|
+
Duncan Robertson [Duncan.Robertson@vsl.com.au] - 64bits fix
|
30
|
+
Ali Rahimi [ali@XCF.Berkeley.EDU] - Sockets code
|
31
|
+
David W. Deley [deleyd@cox.net] - Random numbers
|
32
|
+
|
33
|
+
$Id: check_em01.c,v 2.1 3:47 PM 1/26/2009 $
|
34
|
+
|
35
|
+
******************************************************************************/
|
36
|
+
/** This value is to multiple the Voltage value detected by a fixed constant. (
|
37
|
+
* i.e. if you are using a regulator supply to measure AC voltage)
|
38
|
+
*/
|
39
|
+
const float VOLTAGE_MULTIPLIER = 1.0;
|
40
|
+
const float VOLTAGE_OFFSET = 0.0;
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Tells the plugin to reset contact closure on trigger. Change to 0 to turn this off.
|
44
|
+
*/
|
45
|
+
const int RESETCONTACT = 1;
|
46
|
+
|
47
|
+
|
48
|
+
const int DEFAULTPORT = 80;
|
49
|
+
const char *progname = "check_em01";
|
50
|
+
const char *revision = "$Revision: 2.1 $";
|
51
|
+
const char *copyright = "2009";
|
52
|
+
const char *email = "techhelp@eEsensors.com";
|
53
|
+
|
54
|
+
typedef int SOCKET;
|
55
|
+
typedef enum {E_NET_ERRNO=-1, E_NET_OK=0} NetErrnoType;
|
56
|
+
|
57
|
+
#include <stdio.h>
|
58
|
+
#include <stdlib.h>
|
59
|
+
#include <sys/types.h>
|
60
|
+
#include <sys/socket.h>
|
61
|
+
#include <netinet/in.h>
|
62
|
+
#include <arpa/inet.h>
|
63
|
+
#include <netdb.h>
|
64
|
+
#include <sys/ioctl.h>
|
65
|
+
#include <string.h>
|
66
|
+
#include <errno.h>
|
67
|
+
#include <signal.h>
|
68
|
+
#include <time.h>
|
69
|
+
|
70
|
+
void INThandler(int);
|
71
|
+
|
72
|
+
static NetErrnoType net_errno = E_NET_OK;
|
73
|
+
static int saved_errno = 0;
|
74
|
+
static SOCKET s;
|
75
|
+
|
76
|
+
|
77
|
+
/* Translations between ``net_errno'' values and human readable strings.
|
78
|
+
*/
|
79
|
+
static const char *net_syserrlist[] = {
|
80
|
+
"All was chill"
|
81
|
+
};
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
#ifdef STRERROR_NOT_DEFINED
|
87
|
+
const char *strerror(int errno) { return sys_errlist[errno]; }
|
88
|
+
#endif
|
89
|
+
|
90
|
+
static void NetSetErrno(NetErrnoType e)
|
91
|
+
{
|
92
|
+
if(e == E_NET_ERRNO)saved_errno = errno;
|
93
|
+
net_errno = e;
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
/* NetErrStr()
|
99
|
+
*--------------------------------------------------------------------
|
100
|
+
* Returns a diagnostic message for the last failure.
|
101
|
+
*/
|
102
|
+
const char *NetErrStr()
|
103
|
+
{
|
104
|
+
return net_errno==E_NET_ERRNO ? strerror(saved_errno) :
|
105
|
+
net_syserrlist[net_errno];
|
106
|
+
}
|
107
|
+
|
108
|
+
/* NetErrNo()
|
109
|
+
*--------------------------------------------------------------------
|
110
|
+
* Returns a diagnostic number for the last failure.
|
111
|
+
*/
|
112
|
+
NetErrnoType NetErrNo()
|
113
|
+
{
|
114
|
+
return net_errno;
|
115
|
+
}
|
116
|
+
|
117
|
+
/* NetMakeContact()
|
118
|
+
*--------------------------------------------------------------------
|
119
|
+
* Makes a tcp connection to a host:port pair.
|
120
|
+
*--------------------------------------------------------------------
|
121
|
+
* ``Hostname'' can either be in the form of a hostname or an IP address
|
122
|
+
* represented as a string. If the hostname is not found as it is,
|
123
|
+
* ``hostname'' is assumed to be an IP address, and it is treated as such.
|
124
|
+
*
|
125
|
+
* If the lookup succeeds, a TCP connection is established with the
|
126
|
+
* specified ``port'' number on the remote host and a stream socket is
|
127
|
+
* returned.
|
128
|
+
*
|
129
|
+
* On any sort of error, an error code can be obtained with @NetErrNo()
|
130
|
+
* and a message with @NetErrStr().
|
131
|
+
*/
|
132
|
+
SOCKET
|
133
|
+
NetMakeContact(const char *hname, int port)
|
134
|
+
{
|
135
|
+
int fd;
|
136
|
+
struct sockaddr_in addr;
|
137
|
+
struct hostent *hent;
|
138
|
+
|
139
|
+
fd = socket(AF_INET, SOCK_STREAM, 0);
|
140
|
+
if(fd == -1)
|
141
|
+
{
|
142
|
+
NetSetErrno(E_NET_ERRNO);
|
143
|
+
return -1;
|
144
|
+
}
|
145
|
+
|
146
|
+
|
147
|
+
hent = gethostbyname(hname);
|
148
|
+
if(hent == NULL)
|
149
|
+
addr.sin_addr.s_addr = inet_addr(hname);
|
150
|
+
else
|
151
|
+
memcpy(&addr.sin_addr, hent->h_addr, hent->h_length);
|
152
|
+
addr.sin_family = AF_INET;
|
153
|
+
addr.sin_port = htons(port);
|
154
|
+
|
155
|
+
#ifdef DEBUG
|
156
|
+
fprintf(stderr, "Creating Connection... ");
|
157
|
+
#endif
|
158
|
+
|
159
|
+
if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)))
|
160
|
+
{
|
161
|
+
NetSetErrno(E_NET_ERRNO);
|
162
|
+
return -1;
|
163
|
+
}
|
164
|
+
|
165
|
+
NetSetErrno(E_NET_OK);
|
166
|
+
return fd;
|
167
|
+
}
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
/*********************************************************************************/
|
172
|
+
|
173
|
+
float Exp10(int n)
|
174
|
+
{
|
175
|
+
int i;
|
176
|
+
float result = 1;
|
177
|
+
for(i =n; i; i--)
|
178
|
+
result *= 10;
|
179
|
+
return result;
|
180
|
+
}
|
181
|
+
|
182
|
+
float myatof(const char *s)
|
183
|
+
{
|
184
|
+
float result;
|
185
|
+
int val = 0, dec = 0, n = 0;;
|
186
|
+
int neg = 0;
|
187
|
+
|
188
|
+
/* skip white space */
|
189
|
+
while (*s == ' ' || *s == '\t') {
|
190
|
+
s++;
|
191
|
+
}
|
192
|
+
|
193
|
+
if (*s == '-') {
|
194
|
+
neg = 1;
|
195
|
+
s++;
|
196
|
+
} else if (*s == '+') {
|
197
|
+
s++;
|
198
|
+
}
|
199
|
+
while (*s >= '0' && *s <= '9') {
|
200
|
+
val *= 10;
|
201
|
+
val += *s++ - '0';
|
202
|
+
}
|
203
|
+
result = val;
|
204
|
+
|
205
|
+
if(*s == '.') {
|
206
|
+
*s++;
|
207
|
+
while (*s >= '0' && *s <= '9') {
|
208
|
+
dec *= 10;
|
209
|
+
dec += *s++ - '0';
|
210
|
+
n++;
|
211
|
+
}
|
212
|
+
|
213
|
+
if(n)
|
214
|
+
result += dec/Exp10(n);
|
215
|
+
}
|
216
|
+
|
217
|
+
if (neg) {
|
218
|
+
result = -result;
|
219
|
+
}
|
220
|
+
|
221
|
+
return result;
|
222
|
+
}
|
223
|
+
|
224
|
+
/*
|
225
|
+
This function is an abstraction layer between app and sockets.
|
226
|
+
Currently, it only passes down all the arguments it receives from
|
227
|
+
app. However, in the future, it will be easier to change
|
228
|
+
sockets library and not affect the main app code at all.
|
229
|
+
*/
|
230
|
+
SOCKET connectWebsensor (char* hostname, int port){
|
231
|
+
//SOCKET s;
|
232
|
+
fd_set readfds;
|
233
|
+
|
234
|
+
int i;
|
235
|
+
s = NetMakeContact(hostname,port);
|
236
|
+
if(s==-1) {
|
237
|
+
return -1;
|
238
|
+
}
|
239
|
+
else{
|
240
|
+
/* Make the socket non-blocking */
|
241
|
+
ioctl(s, FIONBIO, 1);
|
242
|
+
FD_ZERO(&readfds);
|
243
|
+
FD_SET(0, &readfds);
|
244
|
+
FD_SET(s, &readfds);
|
245
|
+
return s;
|
246
|
+
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
/*
|
251
|
+
This function is called when a timeout has occurred.
|
252
|
+
It shutsdown the socket and allows the main loop to continue.
|
253
|
+
*/
|
254
|
+
void INThandler(int sig)
|
255
|
+
{
|
256
|
+
signal(SIGALRM, SIG_IGN);
|
257
|
+
shutdown(s, SHUT_RDWR);
|
258
|
+
alarm(CONNECT_TIMEOUT);
|
259
|
+
signal(SIGALRM, INThandler);
|
260
|
+
}
|
261
|
+
|
262
|
+
|
263
|
+
main(int argc, char **argv)
|
264
|
+
{
|
265
|
+
int l, retry, i, random_backoff;
|
266
|
+
long ms;
|
267
|
+
float data;
|
268
|
+
char iobuf[1024], timestr[32], datachar[7];
|
269
|
+
char* pos;
|
270
|
+
char rcvd_checksum, calc_checksum;
|
271
|
+
time_t start_time, cur_time;
|
272
|
+
double r, x;
|
273
|
+
SOCKET contacts;
|
274
|
+
|
275
|
+
progname = argv[0];
|
276
|
+
if(argc < 2 || strcmp(argv[1],"--help") == 0) {
|
277
|
+
print_help();
|
278
|
+
return(3);
|
279
|
+
}
|
280
|
+
|
281
|
+
time(&cur_time);
|
282
|
+
sprintf(timestr, "%s", ctime(&cur_time));
|
283
|
+
|
284
|
+
signal(SIGALRM, INThandler);
|
285
|
+
alarm(CONNECT_TIMEOUT);
|
286
|
+
|
287
|
+
for(retry = 0; retry < MAX_RETRIES; retry++){
|
288
|
+
|
289
|
+
srand((unsigned int)time(NULL));
|
290
|
+
|
291
|
+
r = ( (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
|
292
|
+
x = (r * 20);
|
293
|
+
random_backoff = (int) x;
|
294
|
+
|
295
|
+
#ifdef DEBUG
|
296
|
+
fprintf(stderr, "Sleeping: %d ", 100+random_backoff);
|
297
|
+
#endif
|
298
|
+
for(ms=0; ms < 100+random_backoff; ms++){
|
299
|
+
usleep(5000);
|
300
|
+
}
|
301
|
+
|
302
|
+
|
303
|
+
/* make connection to websensor */
|
304
|
+
s = connectWebsensor(argv[1], DEFAULTPORT);
|
305
|
+
#ifdef DEBUG
|
306
|
+
fprintf(stderr, "Socket created ");
|
307
|
+
#endif
|
308
|
+
|
309
|
+
if(NetErrNo() != 0){
|
310
|
+
#ifdef DEBUG
|
311
|
+
fprintf(stderr, "Could not connect to Websensor because %s, will retry %d more times.\n", NetErrStr(), 10-retry);
|
312
|
+
#endif
|
313
|
+
shutdown(s, SHUT_RDWR);
|
314
|
+
continue;
|
315
|
+
}
|
316
|
+
|
317
|
+
|
318
|
+
/* send HTTP GET request to obtain data */
|
319
|
+
if(argc>2){
|
320
|
+
|
321
|
+
switch (toupper(argv[2][0])){
|
322
|
+
case 'R':
|
323
|
+
write(s, "GET /index.html?eR HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 77);
|
324
|
+
break;
|
325
|
+
|
326
|
+
case 'V':
|
327
|
+
write(s, "GET /index.html?eV HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 77);
|
328
|
+
break;
|
329
|
+
|
330
|
+
default:
|
331
|
+
write(s, "GET /index.html?em123456 HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 83);
|
332
|
+
break;
|
333
|
+
}
|
334
|
+
}
|
335
|
+
|
336
|
+
else{ // Not enough arguments from command line. Use default websensor command.
|
337
|
+
write(s, "GET /index.html?em123456 HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 83);
|
338
|
+
}
|
339
|
+
|
340
|
+
#ifdef DEBUG
|
341
|
+
fprintf(stderr, "Wrote to Socket ");
|
342
|
+
#endif
|
343
|
+
|
344
|
+
l = read(s, iobuf, sizeof(iobuf));
|
345
|
+
|
346
|
+
#ifdef DEBUG
|
347
|
+
fprintf(stderr, "Read from socket ");
|
348
|
+
#endif
|
349
|
+
|
350
|
+
/* No data returned from websensor. Will retry again. */
|
351
|
+
|
352
|
+
if(l<=0){
|
353
|
+
#ifdef DEBUG
|
354
|
+
fprintf(stderr, "No Data Read, will retry %d more times.\n", MAX_RETRIES-retry);
|
355
|
+
#endif
|
356
|
+
shutdown(s, SHUT_RDWR);
|
357
|
+
continue;
|
358
|
+
}
|
359
|
+
|
360
|
+
pos = strstr(iobuf, "<body>");
|
361
|
+
if(pos == 0){
|
362
|
+
printf("Invalid data received.\n");
|
363
|
+
return(3);
|
364
|
+
}
|
365
|
+
|
366
|
+
|
367
|
+
|
368
|
+
//Search for the sensor data string
|
369
|
+
pos = strstr(iobuf, "TF:");
|
370
|
+
if(pos == 0){
|
371
|
+
pos = strstr(iobuf, "TC:");
|
372
|
+
if(pos == 0){
|
373
|
+
#ifdef DEBUG
|
374
|
+
fprintf(stderr, "Using default parsing parameters.\n");
|
375
|
+
#endif
|
376
|
+
pos=&iobuf[167];
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
380
|
+
if(argc>2 && toupper(argv[2][0]) == 'V'){ //If the command was for voltage measurement, try looking for the string 'RV' instead.
|
381
|
+
pos = strstr(iobuf, "RV");
|
382
|
+
if(pos == 0 || pos[1] != 'V'){
|
383
|
+
#ifdef DEBUG
|
384
|
+
fprintf(stderr, "Invalid Data Received. Will retry %d more times.\n", MAX_RETRIES-retry);
|
385
|
+
#endif
|
386
|
+
shutdown(s, SHUT_RDWR);
|
387
|
+
continue;
|
388
|
+
}
|
389
|
+
else{
|
390
|
+
pos = pos - 2;
|
391
|
+
break; //Voltage data looks good
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
pos = pos - 2;
|
396
|
+
/* Unsupported command. OBSOLETE CODE */
|
397
|
+
if(pos[0] == '#'){
|
398
|
+
printf("Invalid Command. Option %c selected may not be available for this websensor.\n", argv[2][0]);
|
399
|
+
return 3;
|
400
|
+
}
|
401
|
+
|
402
|
+
/* The http data is not properly formatted. */
|
403
|
+
if(pos[2] != 'T' && pos[2] != 'R' && pos[1] != 'v'){
|
404
|
+
#ifdef DEBUG
|
405
|
+
fprintf(stderr, "Data input incorrect, will retry %d more times.\n", MAX_RETRIES-retry);
|
406
|
+
#endif
|
407
|
+
shutdown(s, SHUT_RDWR);
|
408
|
+
continue;
|
409
|
+
}
|
410
|
+
else{
|
411
|
+
break; /* All data looks good. Break out of loop. */
|
412
|
+
}
|
413
|
+
}
|
414
|
+
|
415
|
+
time(&cur_time);
|
416
|
+
sprintf(timestr, "%s", ctime(&cur_time));
|
417
|
+
|
418
|
+
/* Retried 3 times earlier and still no good data. Time to exit */
|
419
|
+
if(retry >= MAX_RETRIES){
|
420
|
+
printf("NO DATA\n");
|
421
|
+
return 3;
|
422
|
+
}
|
423
|
+
else{
|
424
|
+
shutdown(s, SHUT_RDWR);
|
425
|
+
}
|
426
|
+
|
427
|
+
if(argc > 2){
|
428
|
+
switch(toupper(argv[2][0])){
|
429
|
+
|
430
|
+
|
431
|
+
case 'G': //Cacti/RRDTool Output TempF:** Humid:**
|
432
|
+
{
|
433
|
+
strncpy(datachar, pos+5, 5);
|
434
|
+
datachar[5] = '\0';
|
435
|
+
data = myatof(datachar);
|
436
|
+
data += 0.01;
|
437
|
+
printf("Temp:%3.2f ", data);
|
438
|
+
//printf("TempUnit:%c ", pos[3]); //prints the temperature unit
|
439
|
+
|
440
|
+
strncpy(datachar, pos+13, 4);
|
441
|
+
datachar[4] = '\0';
|
442
|
+
data = myatof(datachar);
|
443
|
+
data += 0.01;
|
444
|
+
printf("Humid:%3.2f ", data);
|
445
|
+
|
446
|
+
strncpy(datachar, pos+21, 5);
|
447
|
+
datachar[5] = '\0';
|
448
|
+
data = myatof(datachar);
|
449
|
+
data += 0.01;
|
450
|
+
printf("Illum:%3.2f\n", data);
|
451
|
+
|
452
|
+
return(0);
|
453
|
+
}
|
454
|
+
break;
|
455
|
+
|
456
|
+
|
457
|
+
|
458
|
+
case 'T':
|
459
|
+
{
|
460
|
+
strncpy(datachar, pos+5, 5);
|
461
|
+
datachar[5] = '\0';
|
462
|
+
data = myatof(datachar);
|
463
|
+
data += 0.01;
|
464
|
+
if(argc != 7){
|
465
|
+
printf("(No limits specified) Temperature: %s %c | Temp%c=%3.2f\n", datachar, pos[3], pos[3], data);
|
466
|
+
return(0);
|
467
|
+
}
|
468
|
+
if(data < myatof(argv[5]) || data > myatof(argv[6])){
|
469
|
+
printf("CRITICAL ( %s< or >%s ) Temperature: %s %c | Temp%c=%3.2f\n", argv[5], argv[6], datachar, pos[3], pos[3], data);
|
470
|
+
return(2);
|
471
|
+
}
|
472
|
+
else if(data < myatof(argv[3]) || data > myatof(argv[4])){
|
473
|
+
printf("WARNING ( %s< or >%s ) Temperature: %s %c | Temp%c=%3.2f\n", argv[3], argv[4], datachar, pos[3], pos[3], data);
|
474
|
+
return(1);
|
475
|
+
}
|
476
|
+
else{
|
477
|
+
printf("OK Temperature: %s %c | Temp%c=%3.2f\n", datachar, pos[3], pos[3], data);
|
478
|
+
return(0);
|
479
|
+
}
|
480
|
+
}
|
481
|
+
break;
|
482
|
+
|
483
|
+
case 'H':
|
484
|
+
{
|
485
|
+
strncpy(datachar, pos+13, 4);
|
486
|
+
datachar[4] = '\0';
|
487
|
+
data = myatof(datachar);
|
488
|
+
data += 0.01;
|
489
|
+
if(argc != 7){
|
490
|
+
printf("(No limits specified) %s% | Humid=%3.2f\n", datachar, data);
|
491
|
+
return(0);
|
492
|
+
}
|
493
|
+
if(data < myatof(argv[5]) || data > myatof(argv[6])){
|
494
|
+
printf("CRITICAL ( %s< or >%s ) Humidity: %s% | Humid=%3.2f\n", argv[5], argv[6], datachar, data);
|
495
|
+
return(2);
|
496
|
+
}
|
497
|
+
else if(data < myatof(argv[3]) || data > myatof(argv[4])){
|
498
|
+
printf("WARNING ( %s< or >%s ) Humidity: %s% | Humid=%3.2f\n", argv[3], argv[4], datachar, data);
|
499
|
+
return(1);
|
500
|
+
}
|
501
|
+
else{
|
502
|
+
printf("OK Humidity: %s% | Humid=%3.2f\n", datachar, data);
|
503
|
+
return(0);
|
504
|
+
}
|
505
|
+
}
|
506
|
+
break;
|
507
|
+
|
508
|
+
case 'I':
|
509
|
+
{
|
510
|
+
strncpy(datachar, pos+21, 5);
|
511
|
+
datachar[5] = '\0';
|
512
|
+
data = myatof(datachar);
|
513
|
+
data += 0.01;
|
514
|
+
if(argc != 7){
|
515
|
+
printf("(No limits specified) Illumination: %s | Illum=%3.2f\n", datachar, data);
|
516
|
+
return(0);
|
517
|
+
}
|
518
|
+
if(data < myatof(argv[5]) || data > myatof(argv[6])){
|
519
|
+
printf("CRITICAL ( %s< or >%s ) Illumination: %s | Illum=%3.2f\n", argv[5], argv[6], datachar, data);
|
520
|
+
return(2);
|
521
|
+
}
|
522
|
+
else if(data < myatof(argv[3]) || data > myatof(argv[4])){
|
523
|
+
printf("WARNING ( %s< or >%s ) Illumination: %s | Illum=%3.2f\n", argv[3], argv[4], datachar, data);
|
524
|
+
return(1);
|
525
|
+
}
|
526
|
+
else{
|
527
|
+
printf("OK Illumination: %s | Illum=%3.2f\n", datachar,data);
|
528
|
+
return(0);
|
529
|
+
}
|
530
|
+
}
|
531
|
+
break;
|
532
|
+
|
533
|
+
case 'C':
|
534
|
+
{
|
535
|
+
if(iobuf[160] == 'W'){
|
536
|
+
printf("OK Contacts Close. | Contacts=0\n");
|
537
|
+
return(0);
|
538
|
+
}
|
539
|
+
else if(iobuf[160] == 'N'){
|
540
|
+
printf("CRITICAL Contacts Open! | Contacts=1\n");
|
541
|
+
|
542
|
+
/* Reset the Contact Closure back to Closed */
|
543
|
+
if(RESETCONTACT == 1){
|
544
|
+
contacts = connectWebsensor(argv[1], DEFAULTPORT);
|
545
|
+
if(NetErrNo() != 0){
|
546
|
+
fprintf(stderr, "Could not reset contact closure", NetErrStr());
|
547
|
+
shutdown(contacts, SHUT_RDWR);
|
548
|
+
return(2);
|
549
|
+
}
|
550
|
+
write(contacts, "GET /index.html?eL HTTP/1.1\r\nUser-Agent: EsensorsPlugin\r\nHost: localhost\r\n\r\n", 77);
|
551
|
+
l = read(contacts, iobuf, sizeof(iobuf));
|
552
|
+
//printf("%s", iobuf);
|
553
|
+
shutdown(contacts, SHUT_RDWR);
|
554
|
+
}
|
555
|
+
return(2);
|
556
|
+
}
|
557
|
+
else{
|
558
|
+
printf("WARNING Unknown status. Try Reset Device.\n");
|
559
|
+
return(1);
|
560
|
+
}
|
561
|
+
}
|
562
|
+
break;
|
563
|
+
|
564
|
+
case 'R':
|
565
|
+
{
|
566
|
+
strncpy(datachar, pos+4, 6);
|
567
|
+
datachar[6] = '\0';
|
568
|
+
data = myatof(datachar);
|
569
|
+
data += 0.01;
|
570
|
+
if(argc != 7){
|
571
|
+
printf("(No limits specified) Ext. Temperature: %s %c | XTemp%c=%3.2f\n", datachar, pos[3], pos[3], data);
|
572
|
+
return(0);
|
573
|
+
}
|
574
|
+
if(data < myatof(argv[5]) || data > myatof(argv[6])){
|
575
|
+
printf("CRITICAL ( %s< or >%s ) Ext. Temperature: %s %c | XTemp%c=%3.2f\n", argv[5], argv[6], datachar, pos[3], pos[3], data);
|
576
|
+
return(2);
|
577
|
+
}
|
578
|
+
else if(data < myatof(argv[3]) || data > myatof(argv[4])){
|
579
|
+
printf("WARNING ( %s< or >%s ) Ext. Temperature: %s %c | XTemp%c=%3.2f\n", argv[3], argv[4], datachar, pos[3], pos[3], data);
|
580
|
+
return(1);
|
581
|
+
}
|
582
|
+
else{
|
583
|
+
printf("OK Ext. Temperature: %s %c | XTemp%c=%3.2f\n", datachar, pos[3], pos[3], data);
|
584
|
+
return(0);
|
585
|
+
}
|
586
|
+
}
|
587
|
+
break;
|
588
|
+
|
589
|
+
case 'V':
|
590
|
+
{
|
591
|
+
strncpy(datachar, pos+21, 5);
|
592
|
+
datachar[5] = '\0';
|
593
|
+
data = myatof(datachar);
|
594
|
+
data = (data * VOLTAGE_MULTIPLIER) + VOLTAGE_OFFSET;
|
595
|
+
data += 0.01;
|
596
|
+
if(data <= VOLTAGE_OFFSET+(0.5*VOLTAGE_MULTIPLIER)){ //Anything less than VOLTAGE_OFFSET AC should be considered as zero
|
597
|
+
data = 0.00;
|
598
|
+
}
|
599
|
+
|
600
|
+
if(argc != 7){
|
601
|
+
printf("(No limits specified) Voltage: %3.2f V | Voltage=%3.2f\n", data,data);
|
602
|
+
return(0);
|
603
|
+
}
|
604
|
+
if(data < myatof(argv[5]) || data > myatof(argv[6])){
|
605
|
+
printf("CRITICAL ( %s< or >%s ) Voltage: %3.2f V | Voltage=%3.2f\n", argv[5], argv[6], data, data);
|
606
|
+
return(2);
|
607
|
+
}
|
608
|
+
else if(data < myatof(argv[3]) || data > myatof(argv[4])){
|
609
|
+
printf("WARNING ( %s< or >%s ) Voltage: %3.2f V | Voltage=%3.2f\n", argv[3], argv[4], data, data);
|
610
|
+
return(1);
|
611
|
+
}
|
612
|
+
|
613
|
+
else{
|
614
|
+
printf("OK Voltage: %3.2f V | Voltage=%3.2f\n", data,data);
|
615
|
+
return(0);
|
616
|
+
}
|
617
|
+
}
|
618
|
+
|
619
|
+
default :
|
620
|
+
printf("Please choose only 'T', 'H', 'I', 'R', 'V' or 'C'.\n Please refer to README for further instructions.\n");
|
621
|
+
break;
|
622
|
+
}
|
623
|
+
}
|
624
|
+
else{
|
625
|
+
iobuf[195] = 0;
|
626
|
+
fprintf(stderr, "2");
|
627
|
+
printf("%s\t%s", pos+2, timestr);
|
628
|
+
fprintf(stderr, "3");
|
629
|
+
}
|
630
|
+
|
631
|
+
return 0;
|
632
|
+
}
|
633
|
+
|
634
|
+
int print_help (void)
|
635
|
+
{
|
636
|
+
fprintf (stdout, "Copyright (c) 2005-2009 Esensors, Inc <TechHelp@eEsensors.com>\n");
|
637
|
+
fprintf (stdout,("This plugin is written mainly for Nagios/Cacti/RRDTool, but can work standalone too. \nIt returns the HVAC data from the EM01 Websensor\n\n"));
|
638
|
+
print_usage ();
|
639
|
+
return 0;
|
640
|
+
}
|
641
|
+
|
642
|
+
|
643
|
+
|
644
|
+
int print_usage (void)
|
645
|
+
{
|
646
|
+
fprintf(stdout, "usage: \n %s [hostname] [T/H/I] [LowWarning HighWarning LowCritical HighCritical]\n\n", progname);
|
647
|
+
fprintf(stdout, "Only the hostname is mandatory. The rest of the arguments are optional\n");
|
648
|
+
fprintf(stdout, "T is for Temperature data, H for Humidity Data and I for Illumination data\nExamples:\n");
|
649
|
+
fprintf(stdout, "This will return all HVAC data: \n %s 192.168.0.2\n\n", progname);
|
650
|
+
fprintf(stdout, "This will return only Illumination data: \n %s 192.168.0.2 I\n\n", progname);
|
651
|
+
fprintf(stdout, "This will return temperature data with status: \n %s 192.168.0.2 T 65 85 60 90\n\n", progname);
|
652
|
+
fprintf(stdout, "This will return humidity data with status: \n %s 192.168.0.2 H 25.5 50 15 70.5\n\n", progname);
|
653
|
+
fprintf(stdout, "This will return Cacti/RRDTool format: \n %s 192.168.0.2 G\n\n", progname);
|
654
|
+
fprintf(stdout, "For further information, please refer to the README file included with the package\n");
|
655
|
+
fprintf(stdout, "or available for download from http://www.eesensors.com\n");
|
656
|
+
return 0;
|
657
|
+
}
|