logstash-input-snmp 1.2.7 → 1.2.8

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: 755490661919181ec1b348c1b2bade1300a1829be66206b52c203a230c7a00fd
4
- data.tar.gz: b05f137f91e3720ec567f620358c230b8ffe88f96c83d5158a9d05975b3e8ea6
3
+ metadata.gz: 4bcb5f73151249317b6f7b36c028a3f863e7d4a88ba6da977fbc88d255355364
4
+ data.tar.gz: 85ef765af5f93aefb92a8f40d9738101ada3b5173bb9b4b8de3d4ec21a406578
5
5
  SHA512:
6
- metadata.gz: a3af3dba5c77fa3b6bec973a23eb8e3e0f8260287a6aa974d4cde6dd323193086d9767b2725600fc6e9776445e65bcb09e4e85883ffde331fe9e403eeba68edd
7
- data.tar.gz: dfe8509aa8619a38616aeb23f534097f19ce06dd14f2c7d1bcb6874fcbdb14bf01fef88148b4cd06b20ed1fd766a03e81735cc96694ca6e28106ad4be649e0ab
6
+ metadata.gz: 423f10e23a58b04a72c47d70beb06053dedc8d07e1ec1e36c835ca783c6791bb6ccd12d701d3e50bd4ebe173185a4240ae21a93c36d7ecdf38bf4e2ae6d58f25
7
+ data.tar.gz: f452759e6c453d088b002721ae1520b1c22291e16ff609d8f308e57c3e9aa9dc29b008c809a9a6a76e76b00e68c0399fc96b63f23dc066aa403a682f19712f2e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 1.2.8
2
+ - Fixed interval handling to only sleep off the _remainder_ of the interval (if any), and to log a helpful warning when crawling the hosts takes longer than the configured interval [#61](https://github.com/logstash-plugins/logstash-input-snmp/issues/61)
3
+
1
4
  ## 1.2.7
2
5
  - Added integration tests to ensure SNMP server and IPv6 connections [#87](https://github.com/logstash-plugins/logstash-input-snmp/pull/87)
3
6
 
data/docs/index.asciidoc CHANGED
@@ -163,6 +163,7 @@ input {
163
163
  ===== `interval`
164
164
 
165
165
  The `interval` option specifies the polling interval in seconds.
166
+ If polling all configured hosts takes longer than this interval, a warning will be emitted to the logs.
166
167
 
167
168
  * Value type is <<number,number>>
168
169
  * Default value is `30`
@@ -161,9 +161,9 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
161
161
  end
162
162
 
163
163
  def run(queue)
164
- # for now a naive single threaded poller which sleeps for the given interval between
164
+ # for now a naive single threaded poller which sleeps off the remaining interval between
165
165
  # each run. each run polls all the defined hosts for the get and walk options.
166
- while !stop?
166
+ stoppable_interval_runner.every(@interval, "polling hosts") do
167
167
  @client_definitions.each do |definition|
168
168
  result = {}
169
169
  if !definition[:get].empty?
@@ -207,11 +207,13 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
207
207
  queue << event
208
208
  end
209
209
  end
210
-
211
- Stud.stoppable_sleep(@interval) { stop? }
212
210
  end
213
211
  end
214
212
 
213
+ def stoppable_interval_runner
214
+ StoppableIntervalRunner.new(self)
215
+ end
216
+
215
217
  def close
216
218
  @client_definitions.each do |definition|
217
219
  begin
@@ -222,6 +224,9 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
222
224
  end
223
225
  end
224
226
 
227
+ def stop
228
+ end
229
+
225
230
  private
226
231
 
227
232
  OID_REGEX = /^\.?([0-9\.]+)$/
@@ -295,4 +300,45 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
295
300
  def validate_strip!
296
301
  raise(LogStash::ConfigurationError, "you can not specify both oid_root_skip and oid_path_length") if @oid_root_skip > 0 and @oid_path_length > 0
297
302
  end
303
+
304
+ ##
305
+ # The StoppableIntervalRunner is capable of running a block of code at a
306
+ # repeating interval, while respecting the stop condition of the plugin.
307
+ class StoppableIntervalRunner
308
+ ##
309
+ # @param plugin [#logger,#stop?]
310
+ def initialize(plugin)
311
+ @plugin = plugin
312
+ end
313
+
314
+ ##
315
+ # Runs the provided block repeatedly using the provided interval.
316
+ # After executing the block, the remainder of the interval if any is slept off
317
+ # using an interruptible sleep.
318
+ # If no time remains, a warning is emitted to the logs.
319
+ #
320
+ # @param interval_seconds [Integer,Float]
321
+ # @param desc [String] (default: "operation"): a description to use when logging
322
+ # @yield
323
+ def every(interval_seconds, desc="operation", &block)
324
+ until @plugin.stop?
325
+ start_time = Time.now
326
+
327
+ yield
328
+
329
+ duration_seconds = Time.now - start_time
330
+ if duration_seconds >= interval_seconds
331
+ @plugin.logger.warn("#{desc} took longer than the configured interval", :interval_seconds => interval_seconds, :duration_seconds => duration_seconds.round(3))
332
+ else
333
+ remaining_interval = interval_seconds - duration_seconds
334
+ sleep(remaining_interval)
335
+ end
336
+ end
337
+ end
338
+
339
+ # @api private
340
+ def sleep(duration)
341
+ Stud.stoppable_sleep(duration) { @plugin.stop? }
342
+ end
343
+ end
298
344
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-snmp'
3
- s.version = '1.2.7'
3
+ s.version = '1.2.8'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "SNMP input plugin"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -44,12 +44,12 @@ describe LogStash::Inputs::Snmp do
44
44
  end
45
45
 
46
46
  describe "against single snmp server with snmpv2 and udp", :integration => true do
47
- let(:config) { super.merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}]})}
47
+ let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}]})}
48
48
  it_behaves_like "snmp plugin return single event"
49
49
  end
50
50
 
51
51
  describe "against single server with snmpv3 and tcp", :integration => true do
52
- let(:config) { super.merge({
52
+ let(:config) { super().merge({
53
53
  "hosts" => [{"host" => "tcp:snmp1/161", "version" => "3"}],
54
54
  "security_name" => "user_1",
55
55
  "auth_protocol" => "sha",
@@ -63,7 +63,7 @@ describe LogStash::Inputs::Snmp do
63
63
  end
64
64
 
65
65
  describe "invalid user against snmpv3 server", :integration => true do
66
- let(:config) { super.merge({
66
+ let(:config) { super().merge({
67
67
  "hosts" => [{"host" => "tcp:snmp1/161", "version" => "3"}],
68
68
  "security_name" => "user_2",
69
69
  "auth_protocol" => "sha",
@@ -84,7 +84,7 @@ describe LogStash::Inputs::Snmp do
84
84
  end
85
85
 
86
86
  describe "single input plugin on single server with snmpv2 and mix of udp and tcp", :integration => true do
87
- let(:config) { super.merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "tcp:snmp1/161", "community" => "public"}]})}
87
+ let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "tcp:snmp1/161", "community" => "public"}]})}
88
88
  it "should return two events " do
89
89
  plugin.register
90
90
  queue = []
@@ -99,7 +99,7 @@ describe LogStash::Inputs::Snmp do
99
99
  end
100
100
 
101
101
  describe "single input plugin on multiple udp hosts", :integration => true do
102
- let(:config) { super.merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "udp:snmp2/162", "community" => "public"}]})}
102
+ let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "udp:snmp2/162", "community" => "public"}]})}
103
103
  it "should return two events, one per host" do
104
104
  plugin.register
105
105
  queue = []
@@ -189,7 +189,7 @@ describe LogStash::Inputs::Snmp do
189
189
  end
190
190
 
191
191
  describe "single host with tcp over ipv6", :integration => true do
192
- let(:config) { super.merge({"hosts" => [{"host" => "tcp:[2001:3984:3989::161]/161"}]})}
192
+ let(:config) { super().merge({"hosts" => [{"host" => "tcp:[2001:3984:3989::161]/161"}]})}
193
193
  it_behaves_like "snmp plugin return single event"
194
194
  end
195
195
 
File without changes
@@ -171,5 +171,95 @@ describe LogStash::Inputs::Snmp do
171
171
  expect(event.get("host")).to eq("udp:127.0.0.1/161,public")
172
172
  end
173
173
  end
174
+
175
+ context "StoppableIntervalRunner" do
176
+ let(:stop_holder) { Struct.new(:value).new(false) }
177
+
178
+ before(:each) do
179
+ allow(plugin).to receive(:stop?) { stop_holder.value }
180
+ end
181
+
182
+ let(:plugin) do
183
+ double("Plugin").tap do |dbl|
184
+ allow(dbl).to receive(:logger).and_return(double("Logger").as_null_object)
185
+ allow(dbl).to receive(:stop?) { stop_holder.value }
186
+ end
187
+ end
188
+
189
+ subject(:interval_runner) { LogStash::Inputs::Snmp::StoppableIntervalRunner.new(plugin) }
190
+
191
+ context "#every" do
192
+ context "when the plugin is stopped" do
193
+ let(:interval_seconds) { 2 }
194
+ it 'does not yield the block' do
195
+ stop_holder.value = true
196
+ expect { |yielder| interval_runner.every(interval_seconds, &yielder) }.to_not yield_control
197
+ end
198
+ end
199
+
200
+ context "when the yield takes shorter than the interval" do
201
+ let(:duration_seconds) { 1 }
202
+ let(:interval_seconds) { 2 }
203
+
204
+ it 'sleeps off the remainder' do
205
+ allow(interval_runner).to receive(:sleep).and_call_original
206
+
207
+ interval_runner.every(interval_seconds) do
208
+ Kernel::sleep(duration_seconds) # non-stoppable
209
+ stop_holder.value = true # prevent re-runs
210
+ end
211
+
212
+ expect(interval_runner).to have_received(:sleep).with(a_value_within(0.1).of(1))
213
+ end
214
+ end
215
+
216
+ context "when the yield takes longer than the interval" do
217
+ let(:duration_seconds) { 2 }
218
+ let(:interval_seconds) { 1 }
219
+
220
+ it 'logs a warning to the plugin' do
221
+ allow(interval_runner).to receive(:sleep).and_call_original
222
+
223
+ interval_runner.every(interval_seconds) do
224
+ Kernel::sleep(duration_seconds) # non-stoppable
225
+ stop_holder.value = true # prevent re-runs
226
+ end
227
+
228
+ expect(interval_runner).to_not have_received(:sleep)
229
+
230
+ expect(plugin.logger).to have_received(:warn).with(a_string_including("took longer"), a_hash_including(:interval_seconds => interval_seconds, :duration_seconds => a_value_within(0.1).of(duration_seconds)))
231
+ end
232
+ end
233
+
234
+ it 'runs regularly until the plugin is stopped' do
235
+ timestamps = []
236
+
237
+ thread = Thread.new do
238
+ interval_runner.every(1) do
239
+ timestamps << Time.now
240
+ Kernel::sleep(Random::rand(0.8))
241
+ end
242
+ end
243
+
244
+ Kernel::sleep(5)
245
+ expect(thread).to be_alive
246
+
247
+ stop_holder.value = true
248
+ Kernel::sleep(1)
249
+
250
+ aggregate_failures do
251
+ expect(thread).to_not be_alive
252
+ expect(timestamps.count).to be_within(1).of(5)
253
+
254
+ timestamps.each_cons(2) do |previous, current|
255
+ # ensure each start time is very close to 1s after the previous.
256
+ expect(current - previous).to be_within(0.05).of(1)
257
+ end
258
+
259
+ thread.kill if thread.alive?
260
+ end
261
+ end
262
+ end
263
+ end
174
264
  end
175
265
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-snmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.7
4
+ version: 1.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elasticsearch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-05 00:00:00.000000000 Z
11
+ date: 2021-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -416,8 +416,10 @@ files:
416
416
  - spec/fixtures/collision.dic
417
417
  - spec/inputs/integration/it_spec.rb
418
418
  - spec/inputs/snmp/base_client_spec.rb
419
+ - spec/inputs/snmp/interval_runner_spec.rb
419
420
  - spec/inputs/snmp/mib_spec.rb
420
421
  - spec/inputs/snmp_spec.rb
422
+ - vendor/jar-dependencies/org/snmp4j/snmp4j/2.5.11/snmp4j-2.5.11.jar
421
423
  - vendor/jar-dependencies/org/snmp4j/snmp4j/2.8.4/snmp4j-2.8.4.jar
422
424
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
423
425
  licenses:
@@ -441,8 +443,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
441
443
  - !ruby/object:Gem::Version
442
444
  version: '0'
443
445
  requirements: []
444
- rubyforge_project:
445
- rubygems_version: 2.6.13
446
+ rubygems_version: 3.1.6
446
447
  signing_key:
447
448
  specification_version: 4
448
449
  summary: SNMP input plugin
@@ -451,5 +452,6 @@ test_files:
451
452
  - spec/fixtures/collision.dic
452
453
  - spec/inputs/integration/it_spec.rb
453
454
  - spec/inputs/snmp/base_client_spec.rb
455
+ - spec/inputs/snmp/interval_runner_spec.rb
454
456
  - spec/inputs/snmp/mib_spec.rb
455
457
  - spec/inputs/snmp_spec.rb