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.
@@ -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