zabbix_agent 0.1

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.
Files changed (3) hide show
  1. data/README +41 -0
  2. data/zabbix_agent.rb +238 -0
  3. metadata +67 -0
data/README ADDED
@@ -0,0 +1,41 @@
1
+ Zabbix Ruby agent readme
2
+
3
+ This is a pure Ruby implementation of the Zabbix agent. The agent supports
4
+ both the 1.1 and 1.4 (default) agent protocols. Presently the agent only
5
+ supports passive connections. The agent is multi threaded and will spawn a new
6
+ thread for each incoming connection. After a timeout period (default 30s) the
7
+ thread will be killed and the associated socket closed. Effort has been made
8
+ to make the agent itself thread safe, however the agent makes no effort to
9
+ ensure the functions it calls are thread safe. Thus it is important that the
10
+ end user be aware they are responsible for their functions being thread safe.
11
+
12
+ Required Gems:
13
+ bit-struct
14
+
15
+ Use:
16
+ Use of the agent is pretty straight forward.
17
+
18
+ agent = ZabbixAgent.new(nil,10060)
19
+
20
+ This will instantiate a new agent listening on all interfaces on port 10060.
21
+ The key thing to note is that this will not start the agent's main thread.
22
+
23
+ agent.register("myfunc",self.method(:myfunc))
24
+
25
+ This will register a new key/item with the agent. Behind the scenes all keys
26
+ are stored in a Hash which in turn is used to look up which function to call,
27
+ the resul from this function called is then passed back to the Zabbix server.
28
+ The return value must be in string format. The function will be passed
29
+ whatever arguments were received from the Zabbix server, for instance if the
30
+ agent received the following from the server:
31
+ myfunc[1,2,"str"]
32
+ A string of '1,2,3,"str"' would be passed to the function.
33
+
34
+ Starting the agent is as simple as:
35
+ agent.start
36
+
37
+ This method will return as it will spawn two threads, the main loop and the
38
+ watchdog.
39
+
40
+ After the agent has started it is possible to register more items dynamically.
41
+ The item zbx_available will return the current list of items the agent supports.
data/zabbix_agent.rb ADDED
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/ruby
2
+
3
+ #License:: GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.html
4
+ #Copyright:: Copyright (C) 2009,2010 Andrew Nelson nelsonab(at)red-tux(dot)net
5
+ #
6
+ #This program is free software; you can redistribute it and/or
7
+ #modify it under the terms of the GNU General Public License
8
+ #as published by the Free Software Foundation; either version 2
9
+ #of the License, or (at your option) any later version.
10
+ #
11
+ #This program is distributed in the hope that it will be useful,
12
+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ #GNU General Public License for more details.
15
+ #
16
+ #You should have received a copy of the GNU General Public License
17
+ #along with this program; if not, write to the Free Software
18
+ #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
+
20
+ version_header = <<EOF
21
+ ##########################################
22
+ # Subversion information
23
+ # $Id: zabbix_agent.rb 228 2010-10-13 06:23:18Z nelsonab $
24
+ # $Revision: 228 $
25
+ ##########################################
26
+ EOF
27
+
28
+ REVISION=version_header.split("\n")[3].split[2]
29
+
30
+ require 'rubygems'
31
+ require 'thread'
32
+ require 'socket'
33
+
34
+ require 'bit-struct'
35
+
36
+ #Class for holding the 1.4 protocol header
37
+ class ZBX_14 < BitStruct
38
+ char :header, 32, "Zabbix Header"
39
+ unsigned :version, 8, "Protocol Version"
40
+ unsigned :length, 64, :endian=>:little
41
+ rest :data, "Data"
42
+
43
+ initial_value.header='ZBXD'
44
+ initial_value.version=1
45
+
46
+ def set_data(data)
47
+ self.data=data
48
+ self.length=data.length
49
+ end
50
+
51
+ alias initialize_orig initialize
52
+
53
+ def initialize(data)
54
+ initialize_orig
55
+ set_data(data)
56
+ end
57
+ end
58
+
59
+ class WorkerThread < Thread
60
+ attr_reader :starttime
61
+
62
+ def initialize(*args)
63
+ @starttime=Time.now.to_i
64
+ @socket=nil
65
+ args.each {|item|
66
+ @socket = item if item.class==TCPSocket
67
+ }
68
+
69
+ raise "No TCP Socket Found!" if @socket.nil?
70
+
71
+ super(*args)
72
+ end
73
+
74
+ def kill
75
+ @socket.close #This will generate an IOError in the thread
76
+ super
77
+ end
78
+ end
79
+
80
+ class ZabbixAgent
81
+
82
+ attr_accessor :thread_timeout, :watchdog_sleep_timer
83
+
84
+ #Intialize the port and address to listen on
85
+ #address is the address on the local host to listen to
86
+ #port is the port on the local host to listen to
87
+ #protocol_version is one of the following:
88
+ # :ver1_1, :ver1_4
89
+ # This denotes the protocol version to be used when sending the data back.
90
+ # Version 1.4 is the default
91
+ def initialize(address,port,protocol_version=:ver1_4)
92
+ @proto_ver=protocol_version
93
+ @socket = TCPServer.open(address,port)
94
+
95
+ @functions_mutex = Mutex.new
96
+ @workers_mutex = Mutex.new
97
+
98
+ @thread_timeout=30
99
+ @watchdog_sleep_timer=1
100
+
101
+ #setup internal variables
102
+ @functions={} #Hash of function callbacks key(str):function(method)
103
+
104
+ register("agent.version",self.method(:version))
105
+ register("zbx_available",self.method(:show_avail))
106
+ end
107
+
108
+ #setup the main listener thread
109
+ #This function will return the thread created
110
+ def start
111
+ @workers=[]
112
+ @main_thread = Thread.new do
113
+ main
114
+ puts "Main thread quit"
115
+ end
116
+ @watchdog_thread = Thread.new do watchdog end
117
+ end
118
+
119
+ #this is the main loop for the thread.
120
+ def main
121
+ #When a connection comes in, get the string and send it to the dispatcher
122
+ #which will then take care of the request and then go back to listening
123
+ #for a new connection
124
+ #This agent will not spawn new threads for each incoming connection.
125
+ loop do
126
+ t = WorkerThread.new(@socket.accept) {|session|
127
+ worker_method(session)
128
+ }
129
+ @workers_mutex.synchronize {
130
+ @workers+=[t]
131
+ }
132
+ end
133
+ end
134
+
135
+ def watchdog
136
+ loop do
137
+ @workers_mutex.synchronize {
138
+ @workers.each {|thread|
139
+ if thread.status==false
140
+ @workers.delete(thread) # Thread died of natural causes, we don't need to track it anymore
141
+ elsif thread.starttime < Time.now.to_i-@thread_timeout
142
+ puts "Killing #{thread} due to timeout"
143
+ thread.kill
144
+ @workers.delete(thread)
145
+ end
146
+ }
147
+ }
148
+ sleep @watchdog_sleep_timer
149
+ end
150
+ end
151
+
152
+ def worker_method(session)
153
+ puts "starting socket #{session}"
154
+ begin
155
+ str=session.gets
156
+ dispatch(str,session)
157
+ session.close
158
+ rescue IOError
159
+ #IO Errors are likely due to the thread being killed. If there was a better way to
160
+ #trap this I would rather use it.
161
+ end
162
+ end
163
+
164
+ def output11(socket,data)
165
+ socket.print data
166
+ end
167
+
168
+ def output14(socket,data)
169
+ zbx=ZBX_14.new(data)
170
+ socket.send(zbx,0)
171
+ end
172
+
173
+ #Registers a method with the agent.
174
+ #key is of type string, function is of class method
175
+ #When the key is received, the function is called with the arguments from
176
+ #the Zabbix server and is expected to return the value to be sent
177
+ #The class initializer contains a reference to the use of this function
178
+ #The
179
+ def register(key,function)
180
+ @functions_mutex.synchronize {
181
+ @functions.merge!({key,function})
182
+ }
183
+ end
184
+
185
+ def version(args)
186
+ return "Ruby Zabbix agent svn revision:#{REVISION}"
187
+ end
188
+
189
+ def show_avail(args)
190
+ retval=""
191
+ p @functions
192
+ @functions.each_key do |key|
193
+ retval += "#{key}\n"
194
+ end
195
+ p retval
196
+ return retval
197
+ end
198
+
199
+ def dispatch(str,socket)
200
+ matches = (str =~ /(\S+)\[(\S+)\]|(\S+)/)
201
+ if $1.nil?
202
+ func=$3
203
+ arg=nil
204
+ else
205
+ func=$1
206
+ arg=$2
207
+ end
208
+ if @functions.has_key?(func)
209
+ retval=nil
210
+ @functions_mutex.synchronize {
211
+ retval=@functions[func].call(arg)
212
+ }
213
+ else
214
+ socket.print "ZBX_NOTSUPPORTED"
215
+ return
216
+ end
217
+
218
+ case @proto_ver
219
+ when :ver1_1 then output11(socket,retval)
220
+ when :ver1_4 then output14(socket,retval)
221
+ else output11(socket,retval)
222
+ end
223
+ end
224
+ end
225
+
226
+ if __FILE__ == $0
227
+ Thread.abort_on_exception=true
228
+
229
+ agent = ZabbixAgent.new(nil,10060)
230
+ # agent.thread_timeout=30
231
+ agent.start
232
+
233
+ puts "To quit press ctrl-c"
234
+ #we need to have an infinate loop for the thread to work.
235
+ loop do
236
+ sleep 100
237
+ end
238
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zabbix_agent
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - A. Nelson
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-28 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Class for creating Zabbix agents in Ruby
22
+ email: nelsonab@red-tux.net
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - zabbix_agent.rb
31
+ - README
32
+ has_rdoc: true
33
+ homepage: http://trac.red-tux.net/
34
+ licenses:
35
+ - LGPL 2.1
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - .
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project: zabbix_agent
62
+ rubygems_version: 1.3.7
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Class for creating Zabbix agents in Ruby
66
+ test_files: []
67
+