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 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
- availability = 1
335
- else
336
- availability = 0
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.8
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
@@ -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
- attr_reader :uri, :server, :auth, :request_id, :username, :password, :last_response
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
- def authorized?
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
- def authorize!
49
- response = till_response { send_raw_request(authorization_params) }
50
- raise AuthenticationError.new("Could not authenticate with Zabbix API at #{uri}: #{response.error_message}") if response.error?
51
- raise AuthenticationError.new("Malformed response from Zabbix API: #{response.body}") unless response.string?
52
- @auth = response.result
53
- end
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
- def request method, params
70
- authorize! unless authorized?
71
- till_response do
72
- raw_params = {
73
- :jsonrpc => "2.0",
74
- :id => request_id,
75
- :method => method,
76
- :params => params,
77
- :auth => auth
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(' '))