telemetry-snmp 0.2.0 → 0.3.0

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: 1b1cf78bbce21aa906630c1e4430ed67805d3fa1332664c0048976ea12c92746
4
- data.tar.gz: 0365001cb5dd2dd1767c76234e485b18e0f4c5727743489459b69e1832a478bc
3
+ metadata.gz: ed97642826f1f7eae99d08d99d6e98a587a1f2427b7fb35c1fe3bf58a1056118
4
+ data.tar.gz: 5cbf336cb9c75b0e89b578f85a89c2874a6ce4cbaa6662d48edfeb34058ee4a0
5
5
  SHA512:
6
- metadata.gz: 7dbd63ecd438e885bc22080aed5cf5044a7344671600134d93d77d07c89d6a2065b3d1558cfd1b30e84b44e8a718d581ad08d6436ed4af852c16ec43f14715d3
7
- data.tar.gz: f3377fc61d505f6b165f8d85709d2ea7471933f4d3c83ecb2f50d4360c2e992dbc547114d722f344b65a72b1f94db1f633ba445df3bd82780bd9563b6d11b0cd
6
+ metadata.gz: e1692d91324dfa8eb117c81c78d908dc8018e4ccd7e5682feb6bc45a0dd43c884974db9900fddf54a9e9ae5100d11e41f49c20a0ff70b76bb435fd1447708348
7
+ data.tar.gz: 3e9d11ad581fb35eba0e068d61f4d18dfbc8ddcce599392a903ac645f35647fa300148ebaaaa7ac0ff23a22c7efba176630c11db654329587648df132f1e29d1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Telemetry::SNMP
2
2
 
3
+ ## [0.3.0]
4
+ * Updating locking mechanism
5
+ * Allow for parallel collections
6
+ * Adding measurement prefixes
7
+
8
+ ## [0.2.2]
9
+ * Updating logic to use the IP address of the device first and then hostname if ip is null
10
+
11
+ ## [0.2.1]
12
+ Updating logic so it updates the last_polled column at the beginning of the loop
13
+
3
14
  ## [0.2.0]
4
15
  * Fixing ENV variables to not use .
5
16
  * Adding new device lock table for future reference
data/README.md CHANGED
@@ -11,6 +11,16 @@ bundle exec exe/snmp_collector
11
11
  ```
12
12
 
13
13
 
14
+ 1.3.6.1.2.1.47
15
+ 1.3.6.1.2.1.2.2.1.8 = Interface Up/Down
16
+ 1.3.6.1.2.1.2.2.1.10.1 = Interface in counter
17
+ 1.3.6.1.2.1.2.2.1.14 = Interface In Errors
18
+ 1.3.6.1.2.1.2.2.1.16 = Interface out counters
19
+ 1.3.6.1.2.1.2.2.1.20 = Inteface Out Errors
20
+ 1.3.6.1.2.1.31.1.1.1
21
+
22
+ https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClaSCAS
23
+
14
24
  ### Telemetry::Snmp::API
15
25
  The API allows for you to remotely CRUD devices, oids, users, device credentials, etc
16
26
  The Routes are available via a [postman json](https://github.com/Optum/telemetry-snmp/blob/main/telemetry-snmp.json)
@@ -62,6 +62,10 @@ module Telemetry
62
62
  Telemetry::Snmp::Collector.loop_devices
63
63
  end
64
64
 
65
+ get '/poll_next_device' do
66
+ Telemetry::Snmp::Collector.poll_next_device
67
+ end
68
+
65
69
  namespace('/users') { register Telemetry::Snmp::Controller::Users }
66
70
  namespace('/devices/creds') { register Telemetry::Snmp::Controller::DeviceCreds }
67
71
  namespace('/devices') { register Telemetry::Snmp::Controller::Devices }
@@ -21,10 +21,10 @@ module Telemetry
21
21
  def connection(host)
22
22
  return @connections[host.to_sym] if @connections.key? host.to_sym
23
23
 
24
- dataset = Telemetry::Snmp::Data::Model::Device[hostname: host]
24
+ dataset = Telemetry::Snmp::Data::Model::Device.where(hostname: host).or(ip_address: host).first
25
25
 
26
26
  @connections[host.to_sym] = NETSNMP::Client.new(
27
- host: dataset.values[:ip_address],
27
+ host: dataset.values[:hostname],
28
28
  port: dataset.values[:port],
29
29
  username: dataset.device_cred.values[:username],
30
30
  auth_password: dataset.device_cred.values[:auth_password],
@@ -59,7 +59,7 @@ module Telemetry
59
59
  end
60
60
 
61
61
  def grab_oid_metrics(hostname)
62
- device = Telemetry::Snmp::Data::Model::Device[hostname: hostname]
62
+ device = Telemetry::Snmp::Data::Model::Device.where(hostname: hostname).or(ip_address: hostname).first
63
63
  @lines = []
64
64
  Telemetry::Snmp::Data::Model::OIDWalks.where(:active).each do |row|
65
65
  index = {}
@@ -94,6 +94,8 @@ module Telemetry
94
94
 
95
95
  @lines.push line
96
96
  end
97
+ rescue StandardError => e
98
+ Telemetry::Logger.error "#{e.class}: #{hostname}, #{e.message}"
97
99
  end
98
100
 
99
101
  @lines
@@ -2,16 +2,30 @@ module Telemetry
2
2
  module Snmp
3
3
  module Collector
4
4
  class << self
5
- def worker_name
6
- "#{::Socket.gethostname.tr('.', '_')}.#{::Process.pid}.#{Thread.current.object_id}"
5
+ def loop_devices
6
+ count = 0
7
+ Telemetry::Snmp::Data::Model::Device.where(:active).order(:last_polled).each do |row|
8
+ break if count >= 10
9
+ next if row.values[:last_polled].to_i + row.values[:frequency] > Time.now.to_i
10
+ next if device_locked?(row.values[:id])
11
+
12
+ Telemetry::Logger.info "Grabbing metrics for #{row.values[:hostname]}"
13
+ device = Telemetry::Snmp::DeviceCollector.new(row.values[:hostname])
14
+ device.async.collect
15
+ count += 1
16
+ end
7
17
  end
8
18
 
9
- def loop_devices
10
- Telemetry::Snmp::Data::Model::Device.where(:active).each do |row|
19
+ def poll_next_device
20
+ Telemetry::Snmp::Data::Model::Device.where(:active).order(:last_polled).each do |row|
11
21
  next if row.values[:last_polled].to_i + row.values[:frequency] > Time.now.to_i
12
22
  next if device_locked?(row.values[:id])
13
23
 
24
+ Telemetry::Logger.info "Grabbing metrics for #{row.values[:hostname]}"
14
25
  collect(row.values[:id])
26
+ break
27
+ rescue StandardError => e
28
+ Telemetry::Logger.exception(e, level: 'error')
15
29
  end
16
30
  end
17
31
 
@@ -19,81 +33,17 @@ module Telemetry
19
33
  Telemetry::Snmp::Data::Model::DeviceLock.each do |row|
20
34
  next if row.values[:expires] < Sequel::CURRENT_TIMESTAMP
21
35
 
36
+ Telemetry::Logger.warn "removing lock for #{row.values[:hostname]}"
22
37
  row.delete
23
38
  end
24
39
  end
25
40
 
26
41
  def device_locked?(device_id)
27
- !Telemetry::Snmp::Data::Model::DeviceLock[device_id: device_id].nil?
28
- end
29
-
30
- def lock_device(device_id)
31
- return false unless Telemetry::Snmp::Data::Model::DeviceLock[device_id: device_id].nil?
32
-
33
- Telemetry::Snmp::Data::Model::DeviceLock.insert(
34
- worker_name: worker_name,
35
- device_id: device_id,
36
- created: Sequel::CURRENT_TIMESTAMP,
37
- expires: Sequel::CURRENT_TIMESTAMP
38
- )
39
- true
40
- end
41
-
42
- # noinspection RubyArgCount
43
- def unlock_device(device_id)
44
- device = Telemetry::Snmp::Data::Model::DeviceLock[device_id: device_id]
45
- return true if device.nil?
46
-
47
- device.delete
42
+ Telemetry::Snmp::Data::Model::DeviceLock.where(device_id: device_id).count.positive?
48
43
  end
49
44
 
50
- def collect(device_id)
51
- lock_device(device_id)
52
- row = Telemetry::Snmp::Data::Model::Device[device_id]
53
- lines = []
54
- fields = {}
55
- tags = {
56
- hostname: row.values[:hostname],
57
- ip_address: row.values[:ip_address],
58
- env: row.values[:environment],
59
- dc: row.values[:datacenter],
60
- zone: row.values[:zone],
61
- influxdb_node_group: 'snmp',
62
- influxdb_database: 'snmp'
63
- }
64
-
65
- Telemetry::Snmp::Data::Model::OID.each do |oid_row|
66
- break if @quit
67
-
68
- oid_value = Telemetry::Snmp::Client.oid_value(row[:hostname], oid_row.values[:oid])
69
- next if oid_value.nil?
70
- next unless oid_value.is_a?(Integer) || oid_value.is_a?(Float)
71
-
72
- fields[oid_row.values[:name]] =
73
- "#{Telemetry::Snmp::Client.oid_value(row[:hostname], oid_row.values[:oid])}i"
74
- rescue StandardError => e
75
- Telemetry::Logger.error "#{e.class}: #{e.message}"
76
- end
77
-
78
- lines.push Telemetry::Metrics::Parser.to_line_protocol(
79
- measurement: 'palo_alto',
80
- fields: fields,
81
- tags: tags,
82
- timestamp: (DateTime.now.strftime('%Q').to_i * 1000 * 1000)
83
- )
84
-
85
- walker = Telemetry::Snmp::Client.grab_oid_metrics(row.values[:hostname])
86
- Telemetry::Logger.info "Pushing #{walker.count} lines for #{row.values[:hostname]}" unless walker.empty?
87
- Telemetry::Snmp::Publisher.push_lines(walker) unless walker.empty?
88
-
89
- row.update(last_polled: Sequel::CURRENT_TIMESTAMP)
90
- row.save
91
-
92
- Telemetry::Snmp::Publisher.push_lines(lines) unless lines.empty?
93
- unlock_device(device_id)
94
- rescue StandardError => e
95
- Telemetry::Logger.exception(e, level: 'error')
96
- unlock_device(device_id)
45
+ def device_unlocked?(device_id)
46
+ !device_locked?(device_id)
97
47
  end
98
48
  end
99
49
  end
@@ -0,0 +1,8 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table(:oids) do
4
+ add_column :active, Integer, limit: 1, null: false, default: 1, index: true
5
+ add_column :measurement_name, String, null: false, default: 'snmp'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table(:oids) do
4
+ add_column :brand, String
5
+ add_column :type, String
6
+ add_column :role, String
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,186 @@
1
+ module Telemetry
2
+ module Snmp
3
+ class DeviceCollector
4
+ include Concurrent::Async
5
+
6
+ def initialize(host)
7
+ @device = Telemetry::Snmp::Data::Model::Device.where(hostname: host).or(ip_address: host).first
8
+ return if @device.nil?
9
+
10
+ @id = @device.values[:id]
11
+ @hostname = @device.values[:hostname]
12
+ end
13
+
14
+ def connection
15
+ @connection ||= NETSNMP::Client.new(
16
+ host: @device.values[:hostname],
17
+ port: @device.values[:port],
18
+ username: @device.device_cred.values[:username],
19
+ auth_password: @device.device_cred.values[:auth_password],
20
+ auth_protocol: @device.device_cred.values[:auth_protocol].to_sym,
21
+ priv_password: @device.device_cred.values[:priv_password],
22
+ priv_protocol: @device.device_cred.values[:priv_protocol].to_sym,
23
+ security_level: @device.device_cred.values[:security_level].to_sym
24
+ )
25
+ end
26
+
27
+ def collect
28
+ return false unless lock_device
29
+
30
+ @collection_start = Time.now
31
+
32
+ @device.update(last_polled: Sequel::CURRENT_TIMESTAMP)
33
+ @device.save
34
+ @lines = []
35
+ @fields = {}
36
+
37
+ Telemetry::Snmp::Data::Model::OID.where(:active).each do |oid_row|
38
+ oid_value = oid_value(oid_row.values[:oid])
39
+ oid_value = oid_value.to_i if oid_value.is_a?(NETSNMP::Timetick)
40
+
41
+ if oid_value.nil?
42
+ Telemetry::Logger.warn "#{@hostname} nil result for #{oid_row.values[:oid]}"
43
+ next
44
+ end
45
+
46
+ unless oid_value.is_a?(Integer) || oid_value.is_a?(Float)
47
+ Telemetry::Logger.error "#{@hostname} nil result for #{oid_row.values[:oid]} class: #{oid_value.class}, #{oid_value}" # rubocop:disable Layout/LineLength
48
+ next
49
+ end
50
+
51
+ @fields[oid_row.values[:name]] = "#{oid_value}i"
52
+ rescue StandardError => e
53
+ Telemetry::Logger.error "#{e.class}: #{e.message}"
54
+ end
55
+
56
+ @lines.push Telemetry::Metrics::Parser.to_line_protocol(
57
+ measurement: 'palo_alto',
58
+ fields: @fields,
59
+ tags: tags,
60
+ timestamp: (DateTime.now.strftime('%Q').to_i * 1000 * 1000)
61
+ )
62
+
63
+ walker = grab_oid_metrics
64
+ unless walker.empty?
65
+ Telemetry::Logger.info "Pushing #{walker.count} lines for #{@hostname} in #{((Time.now - @collection_start) * 1000).round}ms" # rubocop:disable Layout/LineLength
66
+ end
67
+ Telemetry::Snmp::Publisher.push_lines(walker) unless walker.empty?
68
+ Telemetry::Snmp::Publisher.push_lines(@lines) unless @lines.empty?
69
+ unlock_device
70
+ rescue StandardError => e
71
+ Telemetry::Logger.exception(e, level: 'error')
72
+ unlock_device
73
+ end
74
+
75
+ def oid_value(oid)
76
+ connection.get(oid: oid)
77
+ rescue StandardError
78
+ nil
79
+ end
80
+
81
+ def oid_walk(oid)
82
+ results = []
83
+ connection.walk(oid: oid).each do |oid_code, value|
84
+ hash = { oid_code: oid_code, value: value }
85
+ begin
86
+ ident = NETSNMP::MIB.identifier(oid_code)
87
+
88
+ hash[:identifier] = ident.first
89
+ rescue StandardError
90
+ # literally do nothing
91
+ end
92
+ results.push hash
93
+ end
94
+
95
+ results
96
+ end
97
+
98
+ def grab_oid_metrics
99
+ @lines = []
100
+ Telemetry::Snmp::Data::Model::OIDWalks.where(:active).each do |row|
101
+ index = {}
102
+ oid_walk(row.values[:oid_index]).each do |hash|
103
+ index[hash[:oid_code].delete_prefix("#{row.values[:oid_index]}.")] = hash[:value].gsub(%r{\\/}, '.')
104
+ end
105
+
106
+ timestamp = DateTime.now.strftime('%Q').to_i * 1000 * 1000
107
+ oid_walk(row.values[:oid_walk]).each do |walk|
108
+ key = walk[:oid_code].split('.').last
109
+ next if walk[:value].is_a? String
110
+ next if walk[:value].nil?
111
+
112
+ fields = {}
113
+ fields[walk[:identifier]] = "#{walk[:value]}i"
114
+ tags = {
115
+ hostname: @hostname,
116
+ interface: index[key],
117
+ ip_address: @device.values[:ip_address],
118
+ zone: @device.values[:zone],
119
+ env: @device.values[:environment],
120
+ dc: @device.values[:datacenter],
121
+ influxdb_node_group: 'snmp',
122
+ influxdb_database: 'snmp'
123
+ }
124
+
125
+ line = Telemetry::Metrics::Parser.to_line_protocol(
126
+ measurement: "snmp_#{row.values[:measurement_name]}",
127
+ fields: fields,
128
+ tags: tags,
129
+ timestamp: timestamp
130
+ )
131
+
132
+ @lines.push line
133
+ end
134
+ rescue StandardError => e
135
+ Telemetry::Logger.error "#{e.class}: #{@hostname}, #{e.message}"
136
+ end
137
+
138
+ @lines
139
+ end
140
+
141
+ def device_locked?
142
+ Telemetry::Snmp::Data::Model::DeviceLock.where(device_id: @id).count.positive?
143
+ end
144
+
145
+ def device_unlocked?
146
+ Telemetry::Snmp::Data::Model::DeviceLock.where(device_id: @id).count.zero?
147
+ end
148
+
149
+ def lock_device
150
+ Telemetry::Snmp::Data::Model::DeviceLock.insert(
151
+ worker_name: worker_name,
152
+ device_id: @id,
153
+ created: Sequel::CURRENT_TIMESTAMP,
154
+ expires: Sequel::CURRENT_TIMESTAMP
155
+ )
156
+ true
157
+ end
158
+
159
+ def tags
160
+ tags = {
161
+ hostname: @device.values[:hostname],
162
+ ip_address: @device.values[:ip_address],
163
+ env: @device.values[:environment],
164
+ dc: @device.values[:datacenter],
165
+ zone: @device.values[:zone],
166
+ influxdb_node_group: 'snmp',
167
+ influxdb_database: 'snmp'
168
+ }
169
+ tags.delete_if { |_k, v| v.nil? }
170
+
171
+ tags
172
+ end
173
+
174
+ def unlock_device
175
+ device = Telemetry::Snmp::Data::Model::DeviceLock[device_id: @id]
176
+ return true if device.nil?
177
+
178
+ device.delete
179
+ end
180
+
181
+ def worker_name
182
+ "#{::Socket.gethostname.tr('.', '_')}.#{::Process.pid}.#{Thread.current.object_id}"
183
+ end
184
+ end
185
+ end
186
+ end
@@ -118,7 +118,7 @@ module Telemetry
118
118
  routing_key: 'snmp',
119
119
  persistent: true,
120
120
  mandatory: false,
121
- timstamp: Time.now.to_i,
121
+ timestamp: Time.now.to_i,
122
122
  type: 'metric',
123
123
  content_type: 'application/json',
124
124
  content_encoding: 'identity'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Telemetry
4
4
  module Snmp
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -7,6 +7,7 @@ require 'telemetry/snmp/data'
7
7
  require 'telemetry/snmp/client'
8
8
  require 'telemetry/snmp/publisher'
9
9
  require 'telemetry/snmp/collector'
10
+ require 'telemetry/snmp/device_collector'
10
11
 
11
12
  module Telemetry
12
13
  module Snmp
@@ -18,6 +19,30 @@ module Telemetry
18
19
  Telemetry::Snmp::Client.load_mibs
19
20
  Telemetry::Snmp::Publisher.start!
20
21
  Telemetry::Logger.info 'Telemetry::Snmp bootstrapped!'
22
+ start_expire_devices
23
+ start_collection
24
+ end
25
+
26
+ def start_expire_devices
27
+ @expire_devices_task = Concurrent::TimerTask.new(execution_interval: 300, timeout_interval: 10) do
28
+ Telemetry::Snmp::Collector.unlock_expired_devices
29
+ end
30
+ @expire_devices_task.execute
31
+ end
32
+
33
+ def stop_expire_devices
34
+ @expire_devices_task.stop
35
+ end
36
+
37
+ def start_collection
38
+ @collection_task = Concurrent::TimerTask.new(execution_interval: 10, timeout_interval: 300) do
39
+ Telemetry::Snmp::Collector.loop_devices
40
+ end
41
+ @collection_task.execute
42
+ end
43
+
44
+ def stop_collection
45
+ @collection_task.stop
21
46
  end
22
47
  end
23
48
  end
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency 'concurrent-ruby', '>= 1.1.7'
28
28
  spec.add_dependency 'concurrent-ruby-ext', '>= 1.1.7'
29
29
  spec.add_dependency 'connection_pool', '>= 2.2.3'
30
+ spec.add_dependency 'elastic-apm'
30
31
  spec.add_dependency 'faraday'
31
32
  spec.add_dependency 'faraday_middleware'
32
33
  spec.add_dependency 'faraday-request-timer'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telemetry-snmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-23 00:00:00.000000000 Z
11
+ date: 2021-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 2.2.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: elastic-apm
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: faraday
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -339,6 +353,8 @@ files:
339
353
  - lib/telemetry/snmp/data/migrations/009_create_tag_name_column.rb
340
354
  - lib/telemetry/snmp/data/migrations/010_create_user_audit_table.rb
341
355
  - lib/telemetry/snmp/data/migrations/011_device_locks.rb
356
+ - lib/telemetry/snmp/data/migrations/012_add_measurement_prefix.rb
357
+ - lib/telemetry/snmp/data/migrations/013_add_fields_to_devices.rb
342
358
  - lib/telemetry/snmp/data/models/device.rb
343
359
  - lib/telemetry/snmp/data/models/device_cred.rb
344
360
  - lib/telemetry/snmp/data/models/device_lock.rb
@@ -348,6 +364,7 @@ files:
348
364
  - lib/telemetry/snmp/data/models/oid_walk.rb
349
365
  - lib/telemetry/snmp/data/models/user.rb
350
366
  - lib/telemetry/snmp/data/models/user_audit_log.rb
367
+ - lib/telemetry/snmp/device_collector.rb
351
368
  - lib/telemetry/snmp/mibs/AGENTX-MIB.txt
352
369
  - lib/telemetry/snmp/mibs/AIRPORT-BASESTATION-3-MIB.txt
353
370
  - lib/telemetry/snmp/mibs/BRIDGE-MIB.txt