rubix 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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(' '))
|