rbZabbix 0.1.4 → 0.1.5
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/.gitignore +7 -0
- data/.yardopts +1 -0
- data/ChangeLog.rdoc +0 -0
- data/Gemfile +4 -0
- data/LICENSE +49 -0
- data/README.rdoc +246 -0
- data/Rakefile +11 -0
- data/TODO.rdoc +0 -0
- data/bin/zabbyrb +86 -0
- data/bin/zabbysh +109 -0
- data/lib/rbZabbix.rb +28 -0
- data/lib/rbZabbix/config.rb +44 -0
- data/lib/rbZabbix/connection.rb +152 -0
- data/lib/rbZabbix/exceptions.rb +26 -0
- data/lib/rbZabbix/runner.rb +102 -0
- data/lib/rbZabbix/shell_helpers.rb +102 -0
- data/lib/rbZabbix/version.rb +8 -0
- data/lib/rbZabbix/zclass.rb +316 -0
- data/rbZabbix.gemspec +32 -0
- data/spec/spec_helper.rb +5 -0
- metadata +122 -76
- checksums.yaml +0 -7
data/lib/rbZabbix.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Author:: Farzad FARID (<ffarid@pragmatic-source.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2011-2012 Farzad FARID
|
|
4
|
+
# License:: Simplified BSD License
|
|
5
|
+
|
|
6
|
+
require 'rubygems'
|
|
7
|
+
require 'json'
|
|
8
|
+
require 'net/http'
|
|
9
|
+
require 'net/https'
|
|
10
|
+
require 'openssl'
|
|
11
|
+
require 'uri'
|
|
12
|
+
require 'pp'
|
|
13
|
+
|
|
14
|
+
require 'rbZabbix/version'
|
|
15
|
+
require 'rbZabbix/exceptions'
|
|
16
|
+
require 'rbZabbix/config'
|
|
17
|
+
require 'rbZabbix/zclass'
|
|
18
|
+
require 'rbZabbix/connection'
|
|
19
|
+
require 'rbZabbix/shell_helpers'
|
|
20
|
+
require 'rbZabbix/runner'
|
|
21
|
+
|
|
22
|
+
module Zabby
|
|
23
|
+
def self.init &block
|
|
24
|
+
z = Zabby::Runner.instance
|
|
25
|
+
z.run(&block) if block_given?
|
|
26
|
+
z
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Author:: Farzad FARID (<ffarid@pragmatic-source.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2011-2012 Farzad FARID
|
|
4
|
+
# License:: Simplified BSD License
|
|
5
|
+
|
|
6
|
+
# Configuration setting
|
|
7
|
+
module Zabby
|
|
8
|
+
class Config
|
|
9
|
+
SETTING_LIST = %w{server user password proxy_host proxy_user proxy_password}
|
|
10
|
+
|
|
11
|
+
# Initialize Zabby configuration settings
|
|
12
|
+
# @todo Anything to configure here?
|
|
13
|
+
def initialize
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Display configuration variables
|
|
17
|
+
def list
|
|
18
|
+
puts "Zabby configuration"
|
|
19
|
+
puts "==================="
|
|
20
|
+
SETTING_LIST.each do |k|
|
|
21
|
+
puts "#{k} = #{instance_variable_get("@#{k}")}"
|
|
22
|
+
end
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Dynamic setter and getter methods for the configuration variables.
|
|
27
|
+
# @param [String] name Setting name, ending with "=" in case we are setting a value
|
|
28
|
+
# @param [Array] args Setting value
|
|
29
|
+
# @param [Proc] block Unused
|
|
30
|
+
# @return [Object] Return the value set
|
|
31
|
+
def method_missing(name, *args, &block)
|
|
32
|
+
name = name.to_s.gsub(/=$/, '')
|
|
33
|
+
raise ConfigurationError.new("Unknown setting '#{name}'") if !SETTING_LIST.include?(name.to_s)
|
|
34
|
+
|
|
35
|
+
if args.empty?
|
|
36
|
+
instance_variable_get("@#{name}")
|
|
37
|
+
elsif args.size != 1
|
|
38
|
+
raise ConfigurationError.new("Too many values for '#{name}'")
|
|
39
|
+
else
|
|
40
|
+
instance_variable_set("@#{name}", args.first)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Author:: Farzad FARID (<ffarid@pragmatic-source.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2011-2012 Farzad FARID
|
|
4
|
+
# License:: Simplified BSD License
|
|
5
|
+
|
|
6
|
+
module Zabby
|
|
7
|
+
class Connection
|
|
8
|
+
# Name of the Zabbix RPC script
|
|
9
|
+
JSONRPC_SCRIPT = "/api_jsonrpc.php"
|
|
10
|
+
|
|
11
|
+
attr_reader :uri, :request_path, :user, :password, :proxy_host, :proxy_user, :proxy_password
|
|
12
|
+
attr_reader :auth
|
|
13
|
+
attr_reader :request_id
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
reset
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def reset
|
|
20
|
+
@uri = @user = @password = @proxy_host = @proxy_user = @proxy_password = nil
|
|
21
|
+
@request_id = 0
|
|
22
|
+
@auth = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def login(config)
|
|
26
|
+
return @auth if @auth
|
|
27
|
+
|
|
28
|
+
@uri = URI.parse(config.server)
|
|
29
|
+
@user = config.user
|
|
30
|
+
@password = config.password
|
|
31
|
+
if config.proxy_host
|
|
32
|
+
@proxy_host = URI.parse(config.proxy_host)
|
|
33
|
+
@proxy_user = config.proxy_user
|
|
34
|
+
@proxy_password = config.proxy_password
|
|
35
|
+
end
|
|
36
|
+
@request_path = @uri.path[-4,4] == '.php' ? @uri.path : @uri.path + JSONRPC_SCRIPT
|
|
37
|
+
authenticate
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def logout
|
|
41
|
+
reset
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def logged_in?
|
|
45
|
+
!@auth.nil?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def next_request_id
|
|
49
|
+
@request_id += 1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Authentication key]
|
|
53
|
+
def authenticate
|
|
54
|
+
auth_message = format_message('user', 'login',
|
|
55
|
+
'user' => @user,
|
|
56
|
+
'password' => @password)
|
|
57
|
+
@auth = query_zabbix_rpc(auth_message)
|
|
58
|
+
rescue Zabby::APIError
|
|
59
|
+
# Older Zabbix 1.8.x used a different authentication method. You have to guess...
|
|
60
|
+
auth_message = format_message('user', 'authenticate',
|
|
61
|
+
'user' => @user,
|
|
62
|
+
'password' => @password)
|
|
63
|
+
@auth = query_zabbix_rpc(auth_message)
|
|
64
|
+
rescue Exception => e
|
|
65
|
+
@auth = nil
|
|
66
|
+
raise e
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def format_message(element, action, params = {})
|
|
70
|
+
{
|
|
71
|
+
'jsonrpc' => '2.0',
|
|
72
|
+
'id' => next_request_id,
|
|
73
|
+
'method' => "#{element}.#{action}",
|
|
74
|
+
'params' => params,
|
|
75
|
+
'auth' => @auth
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Perform an authenticated request
|
|
80
|
+
# @return [Object] The Zabbix response (Hash, Boolean, etc.) in JSON format.
|
|
81
|
+
def perform_request(element, action, params)
|
|
82
|
+
raise AuthenticationError.new("Not logged in") if !logged_in?
|
|
83
|
+
|
|
84
|
+
message = format_message(element, action, params)
|
|
85
|
+
query_zabbix_rpc(message)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Prepare a JSON request HTTP Post format
|
|
89
|
+
# @param [Hash] message A hash with all parameters for the Zabbix web service.
|
|
90
|
+
# @return [Net::HTTP::Post] Message ready to be POSTed.
|
|
91
|
+
def request(message)
|
|
92
|
+
req = Net::HTTP::Post.new(@request_path)
|
|
93
|
+
req.add_field('Content-Type', 'application/json-rpc')
|
|
94
|
+
req.body = JSON.generate(message)
|
|
95
|
+
req
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Prepare http object
|
|
99
|
+
def http
|
|
100
|
+
if @proxy_host
|
|
101
|
+
http = Net::HTTP::Proxy(@proxy_host.host, @proxy_host.port, @proxy_user, @proxy_password).new(@uri.host, @uri.port)
|
|
102
|
+
else
|
|
103
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
|
104
|
+
end
|
|
105
|
+
if @uri.scheme == "https"
|
|
106
|
+
http.use_ssl = true
|
|
107
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
108
|
+
end
|
|
109
|
+
http
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Raise a Zabby exception
|
|
113
|
+
# @param [Hash] zabbix_response JSON formatted Zabbix response.
|
|
114
|
+
# @raise [Zabby::AuthenticationError] Authentication error.
|
|
115
|
+
# @raise [zabby::APIError] Generic Zabbix Web Services error.
|
|
116
|
+
def format_exception(zabbix_response)
|
|
117
|
+
error = zabbix_response['error']
|
|
118
|
+
error_message = error['message']
|
|
119
|
+
error_data = error['data']
|
|
120
|
+
error_code = error['code']
|
|
121
|
+
|
|
122
|
+
if error_data == "Login name or password is incorrect"
|
|
123
|
+
raise AuthenticationError.new(error_message, error_code, error_data)
|
|
124
|
+
else
|
|
125
|
+
raise APIError.new(error_message, error_code, error_data)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Query the Zabbix Web Services and extract the JSON response.
|
|
130
|
+
# @param [Hash] message request in JSON format.
|
|
131
|
+
# @return [Object] The Zabbix response (Hash, Boolean, etc.) in JSON format.
|
|
132
|
+
# @raise [Zabby::ResponseCodeError] HTTP error.
|
|
133
|
+
def query_zabbix_rpc(message)
|
|
134
|
+
# Send the request!
|
|
135
|
+
http_response = http.request(request(message))
|
|
136
|
+
|
|
137
|
+
# Check for HTTP errors.
|
|
138
|
+
if http_response.code != "200"
|
|
139
|
+
raise ResponseCodeError.new("Error from #{@uri}", http_response.code, http_response.body)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
zabbix_response = JSON.parse(http_response.body)
|
|
143
|
+
|
|
144
|
+
# Check for Zabbix errors.
|
|
145
|
+
if zabbix_response['error']
|
|
146
|
+
format_exception(zabbix_response)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
zabbix_response['result']
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Author:: Farzad FARID (<ffarid@pragmatic-source.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2011-2012 Farzad FARID
|
|
4
|
+
# License:: Simplified BSD License
|
|
5
|
+
|
|
6
|
+
module Zabby
|
|
7
|
+
class APIError < StandardError
|
|
8
|
+
attr_reader :code, :msg, :data
|
|
9
|
+
|
|
10
|
+
def initialize(msg, code = nil, data = nil)
|
|
11
|
+
@msg = msg
|
|
12
|
+
@code = code
|
|
13
|
+
@data = data
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def message
|
|
17
|
+
text = "#{msg}"
|
|
18
|
+
text += ": #{data}" if data
|
|
19
|
+
text += " (code: #{code})" if code
|
|
20
|
+
text
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
class ResponseCodeError < APIError; end
|
|
24
|
+
class AuthenticationError < APIError; end
|
|
25
|
+
class ConfigurationError < StandardError; end
|
|
26
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Author:: Farzad FARID (<ffarid@pragmatic-source.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2011-2012 Farzad FARID
|
|
4
|
+
# License:: Simplified BSD License
|
|
5
|
+
|
|
6
|
+
require 'singleton'
|
|
7
|
+
begin
|
|
8
|
+
require 'readline'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
# No readline
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module Zabby
|
|
15
|
+
# This is the main interpreter.
|
|
16
|
+
# Additional shell commands are defined in the ShellHelpers module.
|
|
17
|
+
class Runner
|
|
18
|
+
include Singleton
|
|
19
|
+
include ShellHelpers
|
|
20
|
+
|
|
21
|
+
attr_reader :config, :connection
|
|
22
|
+
|
|
23
|
+
def initialize
|
|
24
|
+
@config = Zabby::Config.new
|
|
25
|
+
@connection = Zabby::Connection.new
|
|
26
|
+
@pure_binding = instance_eval "binding"
|
|
27
|
+
|
|
28
|
+
# Configure Readline for the shell, if available
|
|
29
|
+
if Object.const_defined?(:Readline)
|
|
30
|
+
@readline = true
|
|
31
|
+
Readline.basic_word_break_characters = ""
|
|
32
|
+
Readline.completion_append_character = nil
|
|
33
|
+
#Readline.completion_proc = completion_proc
|
|
34
|
+
|
|
35
|
+
$last_res = nil
|
|
36
|
+
else
|
|
37
|
+
# Without Readline the Zabby shell is not available.
|
|
38
|
+
@readline = false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Execute script file and/or instruction block
|
|
43
|
+
# @param [String] command_file Filename containing commands to execute.
|
|
44
|
+
# @param [Proc] block A block containing commands to execute.
|
|
45
|
+
def run(command_file = nil, &block)
|
|
46
|
+
unless command_file.nil?
|
|
47
|
+
commands = File.read(command_file)
|
|
48
|
+
instance_eval(commands, command_file, 1)
|
|
49
|
+
end
|
|
50
|
+
instance_eval(&block) if block_given?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Execute an irb like shell in which we can type Zabby commands.
|
|
54
|
+
def shell
|
|
55
|
+
raise RuntimeError.new("Shell cannot run because 'readline' is missing.") if !@readline
|
|
56
|
+
|
|
57
|
+
puts <<HEADER
|
|
58
|
+
Zabby Shell #{Zabby::VERSION}
|
|
59
|
+
|
|
60
|
+
** This is a simple irb like Zabbix Shell. Multiline commands do not work for e.g. **
|
|
61
|
+
Type "help" for online documentation.
|
|
62
|
+
HEADER
|
|
63
|
+
loop do
|
|
64
|
+
cmd = Readline.readline('zabby> ')
|
|
65
|
+
|
|
66
|
+
exit(0) if cmd.nil? or cmd == 'exit'
|
|
67
|
+
next if cmd == ""
|
|
68
|
+
Readline::HISTORY.push(cmd)
|
|
69
|
+
|
|
70
|
+
execute(cmd)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Run a single command.
|
|
77
|
+
# @param [String] cmd A command to execute in the object's context.
|
|
78
|
+
def execute(cmd)
|
|
79
|
+
res = eval(cmd, @pure_binding)
|
|
80
|
+
$last_res = res
|
|
81
|
+
eval("_ = $last_res", @pure_binding)
|
|
82
|
+
print_result res
|
|
83
|
+
rescue ::Exception => e
|
|
84
|
+
puts "Exception #{e.class} -> #{e.message}"
|
|
85
|
+
e.backtrace.each do |t|
|
|
86
|
+
puts " #{::File.expand_path(t)}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Print a single command's output on the screen using "inspect" if necessary.
|
|
91
|
+
# @param [Object] res Result object to display
|
|
92
|
+
def print_result(res)
|
|
93
|
+
if res.kind_of? String
|
|
94
|
+
puts res
|
|
95
|
+
elsif res.nil?
|
|
96
|
+
# Do not print nil values
|
|
97
|
+
else
|
|
98
|
+
puts "=> #{res.inspect}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Author:: Farzad FARID (<ffarid@pragmatic-source.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2011-2012 Farzad FARID
|
|
4
|
+
# License:: Simplified BSD License
|
|
5
|
+
|
|
6
|
+
module Zabby
|
|
7
|
+
# Useful helper methods for the Zabbix Shell. Methods added to this module
|
|
8
|
+
# are available in the scripting language and commande line.
|
|
9
|
+
# The following instance variable should be available to helper methods:
|
|
10
|
+
# - @config: Zabby::Config instance
|
|
11
|
+
# - @connection: Zabby::Connection instance
|
|
12
|
+
module ShellHelpers
|
|
13
|
+
# Documentation for helpers.
|
|
14
|
+
# Each helper method definition must be preceded by a call to "desc" and a short
|
|
15
|
+
# online help for the method.
|
|
16
|
+
@helpers_doc = {}
|
|
17
|
+
@last_doc = nil
|
|
18
|
+
|
|
19
|
+
# Save the documentation for a method about to be defined.
|
|
20
|
+
# @param text [String] Documentation of the method following the call to "desc"
|
|
21
|
+
def self.desc(text)
|
|
22
|
+
@last_doc = text
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Push helper documentation for the method just defined in a hash.
|
|
26
|
+
# @param [Symbol] method Helper method to document
|
|
27
|
+
# @todo Display functions in alphabetical or arbitrary order.
|
|
28
|
+
def self.method_added(method)
|
|
29
|
+
if @last_doc.nil?
|
|
30
|
+
@helpers_doc[method.id2name] = "** UNDOCUMENTED FUNCTION **"
|
|
31
|
+
else
|
|
32
|
+
@helpers_doc[method.id2name] = @last_doc
|
|
33
|
+
@last_doc = nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Show the Shell helpers documentation
|
|
38
|
+
def self.helpers_doc
|
|
39
|
+
help = <<EOT
|
|
40
|
+
Available commands:
|
|
41
|
+
==================
|
|
42
|
+
|
|
43
|
+
EOT
|
|
44
|
+
@helpers_doc.each do |name, text|
|
|
45
|
+
help += name + ":\n"
|
|
46
|
+
help += '-' * name.size + "\n"
|
|
47
|
+
help += text + "\n\n"
|
|
48
|
+
end
|
|
49
|
+
help
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc %q{Set or query Zabby parameters.
|
|
53
|
+
- "set" without argument displays all parameters.
|
|
54
|
+
- "set <param>" shows the value of <param>
|
|
55
|
+
- "set <param> => <value>" set <param> to <value>}
|
|
56
|
+
def set(key_value = nil)
|
|
57
|
+
if key_value.nil?
|
|
58
|
+
@config.list
|
|
59
|
+
elsif [ String, Symbol ].include?(key_value.class)
|
|
60
|
+
puts "#{key_value} = #{@config.send(key_value)}"
|
|
61
|
+
elsif key_value.instance_of? Hash
|
|
62
|
+
key = key_value.keys.first
|
|
63
|
+
value = key_value[key]
|
|
64
|
+
@config.send(key, value)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc %q{Login to the Zabbix server.
|
|
69
|
+
The parameters 'server', 'user' and 'password' must be defined.}
|
|
70
|
+
def login
|
|
71
|
+
@connection.login(@config)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
desc 'Logout from the Zabbix server'
|
|
75
|
+
def logout
|
|
76
|
+
@connection.logout
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
desc 'Return true if we are connected to the Zabbix server, false otherwise.'
|
|
80
|
+
def logged_in?
|
|
81
|
+
@connection.logged_in?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
desc 'Alias for "logged_in?".'
|
|
85
|
+
alias_method :loggedin?, :logged_in?
|
|
86
|
+
|
|
87
|
+
desc 'Show Zabby version.'
|
|
88
|
+
def version
|
|
89
|
+
Zabby::VERSION
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
desc 'Return the list of available Zabbix Classes (object types).'
|
|
93
|
+
def zabbix_classes
|
|
94
|
+
Zabby::ZClass.zabbix_classes
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
desc 'Show this help text.'
|
|
98
|
+
def help
|
|
99
|
+
puts Zabby::ShellHelpers.helpers_doc
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|