rubix 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +10 -10
- data/VERSION +1 -1
- data/lib/rubix.rb +40 -1
- data/lib/rubix/associations/belongs_to_host.rb +4 -3
- data/lib/rubix/connection.rb +114 -34
- data/lib/rubix/log.rb +45 -1
- data/lib/rubix/models/model.rb +143 -9
- data/lib/rubix/response.rb +85 -13
- data/lib/rubix/sender.rb +122 -18
- data/spec/requests/application_request_spec.rb +36 -38
- data/spec/requests/host_group_request_spec.rb +40 -22
- data/spec/requests/host_request_spec.rb +55 -49
- data/spec/requests/item_request_spec.rb +49 -53
- data/spec/requests/template_request_spec.rb +34 -34
- data/spec/requests/user_macro_request_spec.rb +33 -27
- data/spec/spec_helper.rb +1 -13
- data/spec/support/integration_helper.rb +34 -1
- data/spec/test.yml +11 -3
- metadata +22 -8
data/README.rdoc
CHANGED
@@ -175,14 +175,14 @@ Zabbix. (Credentials and addresses for the Zabbix server and the
|
|
175
175
|
Zabbix API can all be customized; see the <tt>--help</tt> option.)
|
176
176
|
|
177
177
|
# Send one data point -- this is tab separated!
|
178
|
-
$ echo "foo.bar.baz 123" | zabbix_pipe --host='My Zabbix Host'
|
178
|
+
$ echo "foo.bar.baz 123" | zabbix_pipe --host='My Zabbix Host' --host_groups='My Zabbix Servers,Some Other Group'
|
179
179
|
# Send a bunch of data points in a file
|
180
|
-
$ cat my_data.tsv | zabbix_pipe --host='My Zabbix Host'
|
180
|
+
$ cat my_data.tsv | zabbix_pipe --host='My Zabbix Host' --host_groups='My Zabbix Servers,Some Other Group'
|
181
181
|
|
182
182
|
You can also pass the file directly:
|
183
183
|
|
184
184
|
# Send a bunch of data points in a file
|
185
|
-
$ zabbix_pipe --host='My Zabbix Host' my_data.tsv
|
185
|
+
$ zabbix_pipe --host='My Zabbix Host' --host_groups='My Zabbix Servers,Some Other Group' my_data.tsv
|
186
186
|
|
187
187
|
You can also listen from a named pipe. This is useful on a
|
188
188
|
"production" system in which many processes may want to simply and
|
@@ -190,7 +190,7 @@ easily write somewhere without worrying about what happens.
|
|
190
190
|
|
191
191
|
# In the first terminal
|
192
192
|
$ mkfifo /dev/zabbix
|
193
|
-
$ zabbix_pipe --pipe=/dev/zabbix --host='My Zabbix Host'
|
193
|
+
$ zabbix_pipe --pipe=/dev/zabbix --host='My Zabbix Host' --host_groups='My Zabbix Servers,Some Other Group'
|
194
194
|
|
195
195
|
# In another terminal
|
196
196
|
$ echo "foo.bar.baz 123" > /dev/zabbix
|
@@ -204,9 +204,9 @@ This simple block of JSON doesn't really add any power to
|
|
204
204
|
+zabbix_pipe+. What becomes more interesting is that we can wedge in
|
205
205
|
data for *diffferent* hosts, items, &c. when using JSON input:
|
206
206
|
|
207
|
-
$ echo '{"host": "My first host", "data": [{"key": "foo.bar.baz", "value": 123}, {"key": "foo.bar.baz", "value": 101}]}' > /dev/zabbix
|
208
|
-
$ echo '{"host": "My second host", "data": [{"key": "foo.bar.baz", "value": 123}, {"key": "foo.bar.baz", "value": 101}]}' > /dev/zabbix
|
209
|
-
$ echo '{"host": "My third host", "data": [{"key": "foo.bar.baz", "value": 123}, {"key": "foo.bar.baz", "value": 101}]}' > /dev/zabbix
|
207
|
+
$ echo '{"host": "My first host", "host_groups": "My Zabbix Servers,Some Other Group", "data": [{"key": "foo.bar.baz", "value": 123}, {"key": "foo.bar.baz", "value": 101}]}' > /dev/zabbix
|
208
|
+
$ echo '{"host": "My second host", "host_groups": "My Zabbix Servers,Some Other Group", "data": [{"key": "foo.bar.baz", "value": 123}, {"key": "foo.bar.baz", "value": 101}]}' > /dev/zabbix
|
209
|
+
$ echo '{"host": "My third host", "host_groups": "My Zabbix Servers,Some Other Group", "data": [{"key": "foo.bar.baz", "value": 123}, {"key": "foo.bar.baz", "value": 101}]}' > /dev/zabbix
|
210
210
|
|
211
211
|
Rubix will switch hosts on the fly.
|
212
212
|
|
@@ -331,9 +331,9 @@ Chef[http://www.opscode.com/chef/] and so Rubix comes with a
|
|
331
331
|
def measure
|
332
332
|
begin
|
333
333
|
if Net::HTTP.get_response(uri).code.to_i == 200
|
334
|
-
|
335
|
-
|
336
|
-
|
334
|
+
availability = 1
|
335
|
+
else
|
336
|
+
availability = 0
|
337
337
|
end
|
338
338
|
rescue => e
|
339
339
|
availability = 0
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.9
|
data/lib/rubix.rb
CHANGED
@@ -10,15 +10,43 @@ module Rubix
|
|
10
10
|
autoload :Connection, 'rubix/connection'
|
11
11
|
autoload :Response, 'rubix/response'
|
12
12
|
autoload :Sender, 'rubix/sender'
|
13
|
-
|
13
|
+
|
14
|
+
# Set up a <tt>Connection</tt> to a Zabbix API server.
|
15
|
+
#
|
16
|
+
# Only needs to be called once.
|
17
|
+
#
|
18
|
+
# # These are the defaults
|
19
|
+
# Rubix.connect 'localhost', 'admin', 'zabbix'
|
20
|
+
#
|
21
|
+
# # A server running on a custom port with different
|
22
|
+
# # credentials...
|
23
|
+
# Rubix.connect 'my.server.com:8080', 'foobar', 'bazbooz'
|
24
|
+
#
|
25
|
+
# @param [URI,String] server the address of the Zabbix API server to connect to
|
26
|
+
# @param [String] username the username of an existing Zabbix API <tt>User</tt> account with API access
|
27
|
+
# @param [String] password the password for this account
|
28
|
+
# @return [Rubix::Connection]
|
14
29
|
def self.connect server, username=nil, password=nil
|
15
30
|
self.connection = Connection.new(server, username, password)
|
16
31
|
end
|
17
32
|
|
33
|
+
# Explicitly set the connection using a <tt>Rubix::Connection</tt>
|
34
|
+
# object.
|
35
|
+
#
|
36
|
+
# Rubix.connection = Rubix::Connection.new('http://localhost/api_jsonrpc.php', 'admin', 'zabbix')
|
37
|
+
#
|
38
|
+
# @param [Rubix::Connection] connection
|
39
|
+
# @return [Rubix::Connection]
|
18
40
|
def self.connection= connection
|
19
41
|
@connection = connection
|
20
42
|
end
|
21
43
|
|
44
|
+
# Return the current connection to a Zabbix API. Useful for
|
45
|
+
# directly sending queries.
|
46
|
+
#
|
47
|
+
# Rubix.connection.request 'host.get', :filter => { "name" => "foobar" }
|
48
|
+
#
|
49
|
+
# @return [Rubix::Connection]
|
22
50
|
def self.connection
|
23
51
|
@connection ||= Connection.new('http://localhost/api_jsonrpc.php', 'admin', 'zabbix')
|
24
52
|
return @connection if @connection.authorized?
|
@@ -26,10 +54,21 @@ module Rubix
|
|
26
54
|
@connection
|
27
55
|
end
|
28
56
|
|
57
|
+
# Base class for Rubix errors.
|
29
58
|
Error = Class.new(RuntimeError)
|
59
|
+
|
60
|
+
# Errors with connecting to a Zabbix API.
|
30
61
|
ConnectionError = Class.new(Error)
|
62
|
+
|
63
|
+
# Error authenticating with a Zabbix API.
|
31
64
|
AuthenticationError = Class.new(Error)
|
65
|
+
|
66
|
+
# Error in a request to a Zabbix API.
|
32
67
|
RequestError = Class.new(Error)
|
68
|
+
|
69
|
+
# Error detected locally in an API resource that will prevent it
|
70
|
+
# from being saved by the Zabbix API (i.e. - no host group for a
|
71
|
+
# host).
|
33
72
|
ValidationError = Class.new(Error)
|
34
73
|
|
35
74
|
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
module Rubix
|
2
2
|
module Associations
|
3
|
+
|
3
4
|
module BelongsToHost
|
4
|
-
|
5
|
+
|
5
6
|
def host= h
|
6
7
|
return unless h
|
7
8
|
@host = h
|
8
9
|
@host_id = h.id
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def host
|
12
13
|
return @host if @host
|
13
14
|
return unless @host_id
|
@@ -18,7 +19,7 @@ module Rubix
|
|
18
19
|
return unless hid
|
19
20
|
@host_id = hid
|
20
21
|
end
|
21
|
-
|
22
|
+
|
22
23
|
def host_id
|
23
24
|
return @host_id if @host_id
|
24
25
|
return unless @host
|
data/lib/rubix/connection.rb
CHANGED
@@ -6,12 +6,44 @@ require 'rubix/log'
|
|
6
6
|
|
7
7
|
module Rubix
|
8
8
|
|
9
|
+
# Wraps and abstracts the process of connecting to a Zabbix API.
|
9
10
|
class Connection
|
10
11
|
|
11
12
|
include Logs
|
12
13
|
|
13
|
-
|
14
|
+
# @return [URI] The URI for the Zabbix API.
|
15
|
+
attr_reader :uri
|
14
16
|
|
17
|
+
# @return [Net::HTTP] the HTTP server backing the Zabbix API.
|
18
|
+
attr_reader :server
|
19
|
+
|
20
|
+
# @return [String] the authentication token provided by the Zabbix
|
21
|
+
# API for this session.
|
22
|
+
attr_reader :auth
|
23
|
+
|
24
|
+
# @return [Fixnum] the ID of the next request that will be sent.
|
25
|
+
attr_reader :request_id
|
26
|
+
|
27
|
+
# @return [String] the username of the Zabbix account used to authenticate
|
28
|
+
attr_reader :username
|
29
|
+
|
30
|
+
# @return [String] the password of the Zabbix account used to authenticate
|
31
|
+
attr_reader :password
|
32
|
+
|
33
|
+
# @return [Rubix::Response] the last response from the Zabbix API -- useful for logging purposes
|
34
|
+
attr_reader :last_response
|
35
|
+
|
36
|
+
# Set up a connection to a Zabbix API.
|
37
|
+
#
|
38
|
+
# The +uri_or_string+ can be either a string or a <tt>URI</tt>
|
39
|
+
# object.
|
40
|
+
#
|
41
|
+
# The +username+ and +password+ provided must correspond to an
|
42
|
+
# existing Zabbix account with API access enabled.
|
43
|
+
#
|
44
|
+
# @param [URI,String] uri_or_string the address of the Zabbix API server to connect to
|
45
|
+
# @param [String] username the username of an existing Zabbix API <tt>User</tt> account with API access
|
46
|
+
# @param [String] password the password for this account
|
15
47
|
def initialize uri_or_string, username=nil, password=nil
|
16
48
|
self.uri = uri_or_string
|
17
49
|
@username = username || uri.user
|
@@ -19,6 +51,51 @@ module Rubix
|
|
19
51
|
@request_id = 0
|
20
52
|
end
|
21
53
|
|
54
|
+
# Send a request to the Zabbix API. Will return a Rubix::Response
|
55
|
+
# object.
|
56
|
+
#
|
57
|
+
# Documentation on what methods and parameters are available can
|
58
|
+
# be found in the {Zabbix API
|
59
|
+
# documentation}[http://www.zabbix.com/documentation/1.8/api]
|
60
|
+
#
|
61
|
+
# Rubix.connection.request 'host.get', 'filter' => { 'host' => 'foobar' }
|
62
|
+
#
|
63
|
+
# @param [String] method the name of the Zabbix API method
|
64
|
+
# @param [Hash,Array] params parameters for the method call
|
65
|
+
# @retrn [Rubix::Response]
|
66
|
+
def request method, params
|
67
|
+
authorize! unless authorized?
|
68
|
+
till_response do
|
69
|
+
raw_params = {
|
70
|
+
:jsonrpc => "2.0",
|
71
|
+
:id => request_id,
|
72
|
+
:method => method,
|
73
|
+
:params => params,
|
74
|
+
:auth => auth
|
75
|
+
}
|
76
|
+
send_raw_request(raw_params)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Has this connection already been authorized and provided with a
|
81
|
+
# authorization token from the Zabbix API?
|
82
|
+
def authorized?
|
83
|
+
!auth.nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
# Force the connection to execute an authorization request and
|
87
|
+
# renew (or set) the authorization token.
|
88
|
+
def authorize!
|
89
|
+
response = till_response { send_raw_request(authorization_params) }
|
90
|
+
raise AuthenticationError.new("Could not authenticate with Zabbix API at #{uri}: #{response.error_message}") if response.error?
|
91
|
+
raise AuthenticationError.new("Malformed response from Zabbix API: #{response.body}") unless response.string?
|
92
|
+
@auth = response.result
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set the URI for this connection's Zabbix API server.
|
96
|
+
#
|
97
|
+
# @param [String, URI] uri_or_string the address of the Zabbix API.
|
98
|
+
# @return [Net::HTTP]
|
22
99
|
def uri= uri_or_string
|
23
100
|
if uri_or_string.respond_to?(:host)
|
24
101
|
@uri = uri_or_string
|
@@ -29,10 +106,12 @@ module Rubix
|
|
29
106
|
@server = Net::HTTP.new(uri.host, uri.port)
|
30
107
|
end
|
31
108
|
|
32
|
-
|
33
|
-
!auth.nil?
|
34
|
-
end
|
109
|
+
protected
|
35
110
|
|
111
|
+
# The parameters used for constructing an authorization request
|
112
|
+
# with the Zabbix API.
|
113
|
+
#
|
114
|
+
# @return [Hash]
|
36
115
|
def authorization_params
|
37
116
|
{
|
38
117
|
:jsonrpc => "2.0",
|
@@ -45,13 +124,17 @@ module Rubix
|
|
45
124
|
}
|
46
125
|
end
|
47
126
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
127
|
+
# Attempt to execute a query until a non-5xx response is returned.
|
128
|
+
#
|
129
|
+
# 5xx responses can occur because the backend PHP server providing
|
130
|
+
# the Zabbix API can sometimes be unavailable if the serer is
|
131
|
+
# restarting or something like that. We keep trying until that
|
132
|
+
# doesn't happen.
|
133
|
+
#
|
134
|
+
# You shouldn't have to use this method directly -- the
|
135
|
+
# <tt>Rubix::Connection#authorize!</tt> and
|
136
|
+
# <tt>Rubix::Connection#request</tt> methods already use this
|
137
|
+
# functionality.
|
55
138
|
def till_response attempt=1, max_attempts=5, &block
|
56
139
|
response = block.call
|
57
140
|
Rubix.logger.log(Logger::DEBUG, "RECV: #{response.body}") if Rubix.logger
|
@@ -65,21 +148,25 @@ module Rubix
|
|
65
148
|
@last_response = Response.new(response)
|
66
149
|
end
|
67
150
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
}
|
79
|
-
send_raw_request(raw_params)
|
151
|
+
|
152
|
+
# Send the POST request to the Zabbix API.
|
153
|
+
#
|
154
|
+
# @param [Hash, #to_json] raw_params the complete parameters of the request.
|
155
|
+
# @return [Net::HTTP::Response]
|
156
|
+
def send_raw_request raw_params
|
157
|
+
@request_id += 1
|
158
|
+
begin
|
159
|
+
raw_response = server.request(raw_post_request(raw_params))
|
160
|
+
rescue NoMethodError, SocketError => e
|
161
|
+
raise RequestError.new("Could not connect to Zabbix server at #{host_with_port}")
|
80
162
|
end
|
163
|
+
raw_response
|
81
164
|
end
|
82
|
-
|
165
|
+
|
166
|
+
# Generate the raw POST request to send to the Zabbix API
|
167
|
+
#
|
168
|
+
# @param [Hash, #to_json] raw_params the complete parameters of the request.
|
169
|
+
# @return [Net::HTTP::Post]
|
83
170
|
def raw_post_request raw_params
|
84
171
|
json_body = raw_params.to_json
|
85
172
|
Rubix.logger.log(Logger::DEBUG, "SEND: #{json_body}") if Rubix.logger
|
@@ -89,6 +176,9 @@ module Rubix
|
|
89
176
|
end
|
90
177
|
end
|
91
178
|
|
179
|
+
# Used for generating helpful error messages.
|
180
|
+
#
|
181
|
+
# @return [String]
|
92
182
|
def host_with_port
|
93
183
|
if uri.port.nil? || uri.port.to_i == 80
|
94
184
|
uri.host
|
@@ -97,15 +187,5 @@ module Rubix
|
|
97
187
|
end
|
98
188
|
end
|
99
189
|
|
100
|
-
def send_raw_request raw_params
|
101
|
-
@request_id += 1
|
102
|
-
begin
|
103
|
-
raw_response = server.request(raw_post_request(raw_params))
|
104
|
-
rescue NoMethodError, SocketError => e
|
105
|
-
raise RequestError.new("Could not connect to Zabbix server at #{host_with_port}")
|
106
|
-
end
|
107
|
-
raw_response
|
108
|
-
end
|
109
|
-
|
110
190
|
end
|
111
191
|
end
|
data/lib/rubix/log.rb
CHANGED
@@ -2,21 +2,40 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module Rubix
|
4
4
|
|
5
|
+
# Set the Rubix logger. Set to +nil+ to disable all logging.
|
6
|
+
#
|
7
|
+
# @param [Logger] l the logger to use
|
5
8
|
def self.logger= l
|
6
9
|
@logger = l
|
7
10
|
end
|
8
11
|
|
12
|
+
# The current Rubix logger.
|
13
|
+
#
|
14
|
+
# @return [Logger, nil]
|
9
15
|
def self.logger
|
10
16
|
return @logger unless @logger.nil?
|
11
17
|
@logger = default_logger
|
12
18
|
end
|
13
19
|
|
20
|
+
# The default logger.
|
21
|
+
#
|
22
|
+
# @return [Logger]
|
14
23
|
def self.default_logger
|
15
24
|
@logger = Logger.new(default_log_path)
|
16
25
|
@logger.level = default_log_severity
|
17
26
|
@logger
|
18
27
|
end
|
19
28
|
|
29
|
+
# The default logger's severity.
|
30
|
+
#
|
31
|
+
# Will attempt to read from
|
32
|
+
#
|
33
|
+
# - <tt>Settings[:log_level]</tt> if <tt>Settings</tt> is defined (see Configliere[http://github.com/infochimps/configliere])
|
34
|
+
# - the <tt>RUBIX_LOG_LEVEL</tt> environment variable if defined
|
35
|
+
#
|
36
|
+
# The default is 'info'.
|
37
|
+
#
|
38
|
+
# @return [Fixnum]
|
20
39
|
def self.default_log_severity
|
21
40
|
case
|
22
41
|
when defined?(Settings) && Settings[:log_level]
|
@@ -34,6 +53,14 @@ module Rubix
|
|
34
53
|
end
|
35
54
|
end
|
36
55
|
|
56
|
+
# The default logger's path.
|
57
|
+
#
|
58
|
+
# Will attempt to read from
|
59
|
+
#
|
60
|
+
# - <tt>Settings[:log]</tt> if <tt>Settings</tt> is defined (see Configliere[http://github.com/infochimps/configliere])
|
61
|
+
# - the <tt>RUBIX_LOG_PATH</tt> environment variable if defined
|
62
|
+
#
|
63
|
+
# Defaults to writing <tt>stdout</tt>.
|
37
64
|
def self.default_log_path
|
38
65
|
case
|
39
66
|
when defined?(Settings) && Settings[:log]
|
@@ -46,29 +73,46 @@ module Rubix
|
|
46
73
|
$stdout
|
47
74
|
end
|
48
75
|
end
|
49
|
-
|
76
|
+
|
77
|
+
# This module can be included by any class to enable logging to the
|
78
|
+
# <tt>Rubix.logger</tt>.
|
50
79
|
module Logs
|
51
80
|
|
81
|
+
# Write a log message with severity +debug+.
|
82
|
+
#
|
83
|
+
# @param [Array<String>] args
|
52
84
|
def debug *args
|
53
85
|
return unless Rubix.logger
|
54
86
|
Rubix.logger.log(Logger::DEBUG, args.join(' '))
|
55
87
|
end
|
56
88
|
|
89
|
+
# Write a log message with severity +info+.
|
90
|
+
#
|
91
|
+
# @param [Array<String>] args
|
57
92
|
def info *args
|
58
93
|
return unless Rubix.logger
|
59
94
|
Rubix.logger.log(Logger::INFO, args.join(' '))
|
60
95
|
end
|
61
96
|
|
97
|
+
# Write a log message with severity +warn+.
|
98
|
+
#
|
99
|
+
# @param [Array<String>] args
|
62
100
|
def warn *args
|
63
101
|
return unless Rubix.logger
|
64
102
|
Rubix.logger.log(Logger::WARN, args.join(' '))
|
65
103
|
end
|
66
104
|
|
105
|
+
# Write a log message with severity +error+.
|
106
|
+
#
|
107
|
+
# @param [Array<String>] args
|
67
108
|
def error *args
|
68
109
|
return unless Rubix.logger
|
69
110
|
Rubix.logger.log(Logger::ERROR, args.join(' '))
|
70
111
|
end
|
71
112
|
|
113
|
+
# Write a log message with severity +fatal+.
|
114
|
+
#
|
115
|
+
# @param [Array<String>] args
|
72
116
|
def fatal *args
|
73
117
|
return unless Rubix.logger
|
74
118
|
Rubix.logger.log(Logger::FATAL, args.join(' '))
|