zabbix_sender_api 1.0.7 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89e13bebbddda9a40ec5990b685c876cccc4dc535d4f0aa71ee5e9e4c9ff0643
4
- data.tar.gz: 5570e008ccdeae7d5f9866fa4edc6faf71bbc7968f1ee5a5a5f2a6d1c815131a
3
+ metadata.gz: c23c9561bca313a86ed0bbd449c45214ace6db1053214867759dfe131fdf1721
4
+ data.tar.gz: 47a7b93f97b811e81b705c58d93dbb8090cf6266f6866e3daa700de0b3891096
5
5
  SHA512:
6
- metadata.gz: 614555a8b2dd25f742140504eae44f84d28a977888ba7d3583ad8db818ecc646a3302a33893c5a46b92215e744f1e0ce7bed0126b4cfc1ac15d03c8657dd7880
7
- data.tar.gz: 8ca65d78ae038b7ac78a093c9b85df976d3f969d3e115e50c0301f72d4a4664834adc3a2e0cbc79ab38180c4b0e4ea9412ff998aaea51a16ad3c687163808d40
6
+ metadata.gz: 71bfc54efbc387979ebf53e033863e3c3f9a72e8e6580def7dedf3e46f65107f13472fbf03cbf9e74271358edd2d3c1fff4aca474d8dd7519e0db531dd090bfc
7
+ data.tar.gz: 6ffb54e8ae9fb8069865b2b748e605948cf4f3f8e53451b4499f959714a735ee3b57c9fd00e7accdcc65df2f894818cf5564610d9ff0e97ecfdc6cf21102252f
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # 1.1.0
2
+ - added native (socket based) sender logic (Zabbix::Sender::Socket).
3
+ - changed Zabbix::Sender::Pipe's open method from popen to Open3::popen3 to allow capture of stdout stderf and exitA
4
+ - the Socket and Pipe .flush method returns status, sendBatchAtomic relays it from its internal call to flush
5
+ # 1.1.1
6
+ - added ability to override Zabbix::AgentConfiguration search path
7
+ - cleaned up a few issues + the docs
data/README.md CHANGED
@@ -1,39 +1,17 @@
1
1
  # zabbix_sender_api
2
2
 
3
- This gem describes an api that takes some of the drudgery out of the task of putting together bulk data for the zabbix_sender utility.
3
+ This gem provides a model for assembling and sending data to Zabbix via its sender/trapper mechanism. I works both with the zabbix_sender command line utility by itself using a socket connection to a trapper port direct from Ruby.
4
4
 
5
5
  **Detailed documentation for this library is at [rubydoc.info](https://rubydoc.info/gems/zabbix_sender_api)**
6
6
 
7
7
  Look at [the source for the zfstozab gem](https://gitlab.com/svdasein/zfstozab/-/blob/master/exe/zabbix-zfs.rb) for a practical example how to use this api.
8
8
 
9
- zabbix_sender is a command line utility for sending monitoring data to Zabbix server or proxy. On the Zabbix server an item of type Zabbix trapper should be created with corresponding key.
9
+ zabbix_sender_api can send data to zabbix via one of two included connection types:
10
+ * zabbix_sender (this was the original and for quite some time only mode supported)
11
+ * TCP socket connection directly to a Zabbix "trapper" port (as of vers 1.1.0)
10
12
 
11
- * [Zabbix sender manpage](https://www.zabbix.com/documentation/4.0/manpages/zabbix_sender)
12
- * [Zabbix sender CLI help](zabbix-sender-help.md)
13
13
 
14
14
 
15
- zabbix_sender_api's function is to generate data to send to a zabbix_sender instance via stdin where zabbix_sender was invoked with these options:
16
-
17
- ```
18
- -i --input-file input-file Load values from input file. Specify - for
19
- standard input. Each line of file contains
20
- whitespace delimited: <host> <key> <value>.
21
- Specify - in <host> to use hostname from
22
- configuration file or --host argument
23
-
24
- -T --with-timestamps Each line of file contains whitespace delimited:
25
- <host> <key> <timestamp> <value>. This can be used
26
- with --input-file option. Timestamp should be
27
- specified in Unix timestamp format
28
- ```
29
-
30
- It is analogous to:
31
-
32
- \<program that generates sender input\> | zabbix-sender -z \<zabbix-server-or-proxy-ip\> -T -i -
33
-
34
- (You can use this api to just generate output that you then pipe to zabbix_sender yourself if you prefer)
35
-
36
-
37
15
  ## Installation
38
16
 
39
17
  $ gem install zabbix_sender_api
@@ -81,6 +59,7 @@ data.addDiscovery(disco)
81
59
 
82
60
  ### Under the hood
83
61
 
62
+ #### Zabbix::Sender::Pipe method:
84
63
  The zabbix-sender cli utility provides a number of methods by which to insert data into zabbix.
85
64
 
86
65
  * zabbix-sender ... -s zabbixHostName -k keyName -o value (one k-v pair at a time)
@@ -120,3 +99,21 @@ If you wished to use the above lld to actually do some discovery, you'd set thin
120
99
 
121
100
  ![Discovery rule configuration](images/Spectacle.Z29721.png)
122
101
  ![Item prototype configuration](images/Spectacle.l29721.png )
102
+
103
+ #### Zabbix::Sender::Socket method:
104
+
105
+ You can switch between using the Pipe(zabbix_sender) method and the Socket(direct to zabbix via tcp socket) method very simply. Just change:
106
+
107
+ ```
108
+ sender = Zabbix::Sender::Pipe.new
109
+ ```
110
+ to
111
+ ```
112
+ sender = Zabbix::Sender::Socket.new
113
+ ```
114
+
115
+ If you were specifiying a path to zabbix_sender using the Pipe method, remove that. If you're using a socket other than 10051 on your server/proxy, you'll want to add the port: option
116
+
117
+ The socket method doesn't support sending multiple batches between flushes, so you should just use sendBatchAtomic(aBatch) with the Socket method.
118
+
119
+ You don't have to change anything else - the rest of zabbix_sender_api works exactly the same with the Socket method, save for that it's arguably more efficient with system resources. The only drawback might be if zabbix changes the sender protocol. At that point until this library is update to support the change you can revert to the Pipe/zabbix_sender method if needed (if they change the command line switches radically there, changes will be required in this library as well).
data/bin/bundle ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'bundle' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "rubygems"
12
+
13
+ m = Module.new do
14
+ module_function
15
+
16
+ def invoked_as_script?
17
+ File.expand_path($0) == File.expand_path(__FILE__)
18
+ end
19
+
20
+ def env_var_version
21
+ ENV["BUNDLER_VERSION"]
22
+ end
23
+
24
+ def cli_arg_version
25
+ return unless invoked_as_script? # don't want to hijack other binstubs
26
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27
+ bundler_version = nil
28
+ update_index = nil
29
+ ARGV.each_with_index do |a, i|
30
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31
+ bundler_version = a
32
+ end
33
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34
+ bundler_version = $1
35
+ update_index = i
36
+ end
37
+ bundler_version
38
+ end
39
+
40
+ def gemfile
41
+ gemfile = ENV["BUNDLE_GEMFILE"]
42
+ return gemfile if gemfile && !gemfile.empty?
43
+
44
+ File.expand_path("../../Gemfile", __FILE__)
45
+ end
46
+
47
+ def lockfile
48
+ lockfile =
49
+ case File.basename(gemfile)
50
+ when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51
+ else "#{gemfile}.lock"
52
+ end
53
+ File.expand_path(lockfile)
54
+ end
55
+
56
+ def lockfile_version
57
+ return unless File.file?(lockfile)
58
+ lockfile_contents = File.read(lockfile)
59
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60
+ Regexp.last_match(1)
61
+ end
62
+
63
+ def bundler_version
64
+ @bundler_version ||=
65
+ env_var_version || cli_arg_version ||
66
+ lockfile_version
67
+ end
68
+
69
+ def bundler_requirement
70
+ return "#{Gem::Requirement.default}.a" unless bundler_version
71
+
72
+ bundler_gem_version = Gem::Version.new(bundler_version)
73
+
74
+ requirement = bundler_gem_version.approximate_recommendation
75
+
76
+ return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
77
+
78
+ requirement += ".a" if bundler_gem_version.prerelease?
79
+
80
+ requirement
81
+ end
82
+
83
+ def load_bundler!
84
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
85
+
86
+ activate_bundler
87
+ end
88
+
89
+ def activate_bundler
90
+ gem_error = activation_error_handling do
91
+ gem "bundler", bundler_requirement
92
+ end
93
+ return if gem_error.nil?
94
+ require_error = activation_error_handling do
95
+ require "bundler/version"
96
+ end
97
+ return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
98
+ warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
99
+ exit 42
100
+ end
101
+
102
+ def activation_error_handling
103
+ yield
104
+ nil
105
+ rescue StandardError, LoadError => e
106
+ e
107
+ end
108
+ end
109
+
110
+ m.load_bundler!
111
+
112
+ if m.invoked_as_script?
113
+ load Gem.bin_path("bundler", "bundle")
114
+ end
data/bin/byebug ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'byebug' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("byebug", "byebug")
data/bin/coderay ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'coderay' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("coderay", "coderay")
data/bin/example.rb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'example.rb' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("zabbix_sender_api", "example.rb")
data/bin/pry ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'pry' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("pry", "pry")
data/bin/rake ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rake", "rake")
@@ -1,20 +1,28 @@
1
1
  module Zabbix
2
2
  require 'socket'
3
3
  require 'json'
4
+ require 'open3'
4
5
 
5
6
  ##
6
7
  # AgentConfiguration holds data that's scraped from a zabbix_agentd config file. It's
7
- # initialized when the gem is required. If it finds configuration you can
8
+ # initialized when the gem is required. You may optionally re-initialize the
9
+ # class with your own list of paths to search. If it finds configuration you can
8
10
  # access it with class methods. This is not meant to be instantiated.
9
-
11
+ #
10
12
  class AgentConfiguration
11
- def self.initialize
12
- @agentConfPaths = [
13
+ ##
14
+ # You may optionally pass an array of full paths to agent conf files to look for
15
+ # during initialization. By default some common places are checked, but
16
+ # you can specify your own. If you call this you'll re-initialize the class, which
17
+ # will scan for values in any of the listed files it happens to find.
18
+ #
19
+ def self.initialize(paths: [
13
20
  '/etc/zabbix/zabbix_agentd.conf',
14
21
  '/usr/local/etc/zabbix/zabbix_agentd.conf',
15
22
  '/opt/zabbix/etc/zabbix_agentd.conf',
16
23
  '/opt/zabbix/conf/zabbix_agentd.conf'
17
- ]
24
+ ])
25
+ @agentConfPaths = paths
18
26
 
19
27
  @proxy = nil
20
28
  @hostname = nil
@@ -63,50 +71,155 @@ module Sender
63
71
  require 'set'
64
72
 
65
73
  ##
66
- # Pipe instances encapsulate communication to a running instance of zabbix_sender
67
- # via a pipe to STDIN. If you want your program to send data itself (as opposed
68
- # to say printing stdin lines that you can then pipe to zabbix_sender yourself),
69
- # you'll want to make an instance of this
70
- #
71
- class Pipe
72
- ##
73
- # pipe file handle object
74
- attr_reader :pipe
74
+ # Connection is an abstract class that defines the basis of specific
75
+ # connection types (Pipe, Socket). It is not meant to be instantiated
76
+ # on its own.
77
+ class Connection
75
78
  ##
76
79
  # target host (server or proxy) name or ip
77
80
  attr_reader :targetHost
81
+ attr_reader :pipe
82
+ ##
83
+ # Initialize a new Connector object. Proxy is optional.
84
+ #
85
+ #
86
+ # An attempt is made to provide sane default values. If you have a zabbix_agentd.conf
87
+ # file in one of the usual places and zabbix_sender is on your path, it'll probably
88
+ # just work
89
+ #
90
+ def initialize(proxy: Zabbix::AgentConfiguration.zabbixProxy)
91
+ @targetHost = proxy
92
+ @pipe = nil
93
+ end
94
+ ##
95
+ # Aborts execution if directly called. Subclasses must override.
96
+ #
97
+ def open
98
+ abort("Call to abstract method Connection::open")
99
+ end
100
+ ##
101
+ # Aborts execution if directly called. Subclasses must override.
102
+ #
103
+ def sendBatch(aBatch)
104
+ abort("Call to abstract method Connection::sendBatch(aBatch)")
105
+ end
106
+ ##
107
+ # Send a Batch instance to zabbix (via zabbix_sender). This opens the pipe,
108
+ # writes the data to the pipe, and closes the pipe all in one go.
109
+ #
110
+ def sendBatchAtomic(aBatch)
111
+ self.open
112
+ self.sendBatch(aBatch)
113
+ return self.flush
114
+ end
115
+ ##
116
+ # Closes the zabbix_sender pipe if it's open
117
+ #
118
+ def flush
119
+ if @pipe and not @pipe.closed?
120
+ @pipe.close
121
+ end
122
+ end
123
+ end
124
+
125
+ ##
126
+ # Socket instances enable TCPSocket based communication with a zabbix trapper server instance
127
+ #
128
+ class Socket < Connection
129
+ attr_reader :port
130
+ ##
131
+ # Create a new socket connection object. Both proxy and port are optional.
132
+ #
133
+ # An attempt is made to provide sane default values. If you have a zabbix_agentd.conf
134
+ # file in one of the usual places and zabbix_sender is on your path, it'll probably
135
+ # just work
136
+ #
137
+ def initialize(proxy: Zabbix::AgentConfiguration.zabbixProxy, port: 10051)
138
+ super(proxy: proxy)
139
+ @port = port
140
+ @lastres = nil
141
+ end
78
142
  ##
79
- # path to the zabbix_sender executable
80
- attr_reader :exePath
143
+ # Open tcp socket to target proxy/server
144
+ #
145
+ def open
146
+ @pipe = TCPSocket.new(@targetHost, @port)
147
+ end
148
+ ##
149
+ # Send aBatch to zabbix via the socket. Note that zabbix will close
150
+ # the connection after you send a complete message, so you
151
+ # *can't* do this:
152
+ #
153
+ # socket.open
154
+ # socket.sendBatch(a)
155
+ # socket.sendBatch(b) <-- this will blow up
156
+ # socket.flush
157
+ #
158
+ # ...as you can with Zabbix::Sender::Pipe. You're really best off
159
+ # just using sendBatchAtomic for sockets.
160
+ #
161
+ # This assumes that the socket is already open.
162
+ #
163
+ def sendBatch(aBatch)
164
+ header = %Q(ZBXD\x01)
165
+ data = aBatch.to_senderstruct.to_json
166
+ blob = %Q(#{header}#{[data.bytesize].pack("Q").force_encoding("UTF-8")}#{data})
167
+ @pipe.write(blob)
168
+ respHeader = @pipe.read(header.bytesize + 8)
169
+ datalen = respHeader[header.bytesize, 8].unpack("Q")[0]
170
+ @lastres = JSON.parse(@pipe.read(datalen))
171
+ end
172
+
173
+ def flush
174
+ super
175
+ return @lastres
176
+ end
177
+ end
81
178
 
179
+ ##
180
+ # Pipe instances utilize communication to a running instance of zabbix_sender
181
+ # via a pipe to STDIN. If you want your program to send data itself (as opposed
182
+ # to say printing stdin lines that you can then pipe to zabbix_sender yourself),
183
+ # you'll want to make an instance of this
184
+ #
185
+ class Pipe < Connection
82
186
 
83
187
  ##
84
- # Initialize a new Sender object. Both proxy: and path: are optional.
188
+ # Create a new Pipe object. Both proxy: and path: are optional.
85
189
  #
86
190
  # An attempt is made to provide sane default values. If you have a zabbix_agentd.conf
87
191
  # file in one of the usual places and zabbix_sender is on your path, it'll probably
88
192
  # just work
89
193
  #
90
194
  def initialize(proxy: Zabbix::AgentConfiguration.zabbixProxy, path: 'zabbix_sender')
91
- @targetHost = proxy
195
+ super(proxy: proxy)
196
+ @wait_thr = @stdout = @stderr = nil
92
197
  @exePath = path
93
- @pipe = nil
94
198
  end
95
199
 
96
200
  ##
97
201
  # Opens a pipe to the zabbix_sender command with appropriate options specified.
98
202
  # If the pipe is already opened when this command is called, it is first closed
99
203
  # via a call to flush
204
+ #
100
205
  def open
101
206
  self.flush
102
- @pipe = IO.popen(%Q(#{@exePath} -z #{@targetHost} -vv -T -i-),'w')
207
+ #@pipe = IO.popen(%Q(#{@exePath} -z #{@targetHost} -vv -T -i-),'w')
208
+ cmd = %Q(#{@exePath} -z #{@targetHost} -vv -T -i-)
209
+ @pipe,@stdout,@stderr,@wait_thr = Open3.popen3(cmd)
103
210
  end
104
211
 
105
212
  ##
106
- # Closes the zabbix_sender pipe if it's open
213
+ # Closes the open3 pipe stuff. We need this override method as
214
+ # closing an open3 pipe requires some extra work.
107
215
  def flush
108
216
  if @pipe and not @pipe.closed?
109
217
  @pipe.close
218
+ stdout = @stdout.read
219
+ stderr = @stderr.read
220
+ @stdout.close
221
+ @stderr.close
222
+ return {stdout: stdout, stderr: stderr, success: @wait_thr.value.success?}
110
223
  end
111
224
  end
112
225
 
@@ -118,14 +231,6 @@ class Pipe
118
231
  @pipe.puts(aBatch.to_senderline)
119
232
  end
120
233
 
121
- ##
122
- # Send a Batch instance to zabbix (via zabbix_sender). This opens the pipe,
123
- # writes the data to the pipe, and closes the pipe all in one go.
124
- def sendBatchAtomic(aBatch)
125
- self.open
126
- self.sendBatch(aBatch)
127
- self.flush
128
- end
129
234
 
130
235
  end
131
236
 
@@ -165,6 +270,21 @@ class ItemData
165
270
  end
166
271
  return %Q("#{@hostname}" #{@key} #{@timestamp.to_i} #{@value}\n)
167
272
  end
273
+ ##
274
+ # Render the ItemData instance as an object suitable for conversion to json, for socket transmission
275
+ def to_senderstruct
276
+ if @timestamp.to_i == 0
277
+ puts %Q("#{@hostname}" #{@key} #{@timestamp.to_i} #{@value}\n)
278
+ abort("Attempt was made to render a timestamp of zero. You DO NOT want this - it can kill db performance. Fix it.")
279
+ else
280
+ return item = {
281
+ host: @hostname,
282
+ key: @key,
283
+ value: @value,
284
+ clock: @timestamp.to_i
285
+ }
286
+ end
287
+ end
168
288
  end
169
289
 
170
290
  ##
@@ -204,12 +324,18 @@ class Discovery < ItemData
204
324
  def to_discodata
205
325
  disco = { 'data'=>Array.new }
206
326
  disco['data'] = @entities.to_a
207
- return disco.to_json
327
+ return disco
208
328
  end
209
329
  ##
210
330
  # Render this discovery instance as a zabbix_sender line.
211
331
  #
212
332
  def to_senderline
333
+ @value = self.to_discodata.to_json
334
+ super
335
+ end
336
+ ##
337
+ # Render this discovery instance as an object suitable for conversion to json for socket transmission
338
+ def to_senderstruct
213
339
  @value = self.to_discodata
214
340
  super
215
341
  end
@@ -282,6 +408,16 @@ class Batch
282
408
  def to_senderline
283
409
  @data.collect {|line| line.to_senderline}.join
284
410
  end
411
+ ##
412
+ # Render this batch as a json object
413
+ #
414
+ def to_senderstruct
415
+ return batch = {
416
+ request: "sender data",
417
+ data: @data.collect {|item| item.to_senderstruct},
418
+ clock: @time.to_i
419
+ }
420
+ end
285
421
  end
286
422
 
287
423
  end # module Sender
@@ -1,3 +1,3 @@
1
1
  module ZabbixSenderApi
2
- VERSION = "1.0.7"
2
+ VERSION = "1.1.1"
3
3
  end
@@ -4,5 +4,8 @@ require "json"
4
4
 
5
5
  module ZabbixSenderApi
6
6
  class Error < StandardError; end
7
+ ##
8
+ # Note: You can call this again yourself with a path: parameter
9
+ # to override the default list if need be.
7
10
  Zabbix::AgentConfiguration.initialize
8
11
  end
@@ -6,8 +6,8 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ["Dave Parker"]
7
7
  spec.email = ["daveparker01@gmail.com"]
8
8
 
9
- spec.summary = %q{Ruby frontend to the zabbix_sender command line tool}
10
- spec.description = %q{This gem describes an api to the zabbix sender facility. It saves tons of hassle when you're cranking out custom polling logic}
9
+ spec.summary = %q{Ruby library that greatly simplifies sending data to Zabbix via the sender/trapper protocol}
10
+ spec.description = %q{This gem implements an api that abstracts the zabbix sender/trapper mechanism. It saves tons of time when you're cranking out custom polling logic}
11
11
  spec.homepage = "https://rubydoc.info/gems/zabbix_sender_api"
12
12
  spec.license = "MIT"
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = "https://gitlab.com/svdasein/zabbix_sender_api"
19
+ spec.metadata["changelog_uri"] = "https://gitlab.com/svdasein/zabbix_sender_api/-/blob/master/CHANGELOG.md"
20
+
19
21
 
20
22
  # Specify which files should be added to the gem when it is released.
21
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zabbix_sender_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Parker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-09 00:00:00.000000000 Z
11
+ date: 2021-09-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: This gem describes an api to the zabbix sender facility. It saves tons
14
- of hassle when you're cranking out custom polling logic
13
+ description: This gem implements an api that abstracts the zabbix sender/trapper mechanism.
14
+ It saves tons of time when you're cranking out custom polling logic
15
15
  email:
16
16
  - daveparker01@gmail.com
17
17
  executables:
@@ -20,11 +20,18 @@ extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
22
  - ".gitignore"
23
+ - CHANGELOG.md
23
24
  - Gemfile
24
25
  - LICENSE.txt
25
26
  - README.md
26
27
  - Rakefile
28
+ - bin/bundle
29
+ - bin/byebug
30
+ - bin/coderay
27
31
  - bin/console
32
+ - bin/example.rb
33
+ - bin/pry
34
+ - bin/rake
28
35
  - bin/setup
29
36
  - exe/example.rb
30
37
  - images/.gitkeep
@@ -42,6 +49,7 @@ metadata:
42
49
  allowed_push_host: https://rubygems.org
43
50
  homepage_uri: https://rubydoc.info/gems/zabbix_sender_api
44
51
  source_code_uri: https://gitlab.com/svdasein/zabbix_sender_api
52
+ changelog_uri: https://gitlab.com/svdasein/zabbix_sender_api/-/blob/master/CHANGELOG.md
45
53
  post_install_message:
46
54
  rdoc_options: []
47
55
  require_paths:
@@ -60,5 +68,6 @@ requirements: []
60
68
  rubygems_version: 3.1.6
61
69
  signing_key:
62
70
  specification_version: 4
63
- summary: Ruby frontend to the zabbix_sender command line tool
71
+ summary: Ruby library that greatly simplifies sending data to Zabbix via the sender/trapper
72
+ protocol
64
73
  test_files: []