zabbix_agent 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+