sensu-plugins-outlyer 0.3.0 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 365da328847c25dfa74e89ed2a70c06722d1412a
4
- data.tar.gz: 54d8abc05c44b0c698286db38c26da254bf85c86
3
+ metadata.gz: 06fefcec9b4ad9d5c308b13f85f547b0bd4b8b53
4
+ data.tar.gz: e2b9db23d1252b2eeb1874eb3fd13a5c5290af91
5
5
  SHA512:
6
- metadata.gz: db2bc31a8f84243a8ed3871296678743f303bc18950a9b1ffe6cd071e8a18c101d2411166c9fbc88cccac13e92478aa1958de5a477ef3085131e1186e3bbbb76
7
- data.tar.gz: 65193231c64a67b0cf57352057bc8728ec2be48acf1a33ced0f1650b0bfcd65e20c2e6cb5b9d0d5a916095acf94ae6f9064e8dcfd61c0780d1b8277f247efef7
6
+ metadata.gz: 2219ff7def5ba400bac2ed178d395ce94d786b95b3dc2102a5a86f5ae7c4c440fc0bfa7476ae663597425cea75f46ddd0d852b3c46804fa13555f8063be44da4
7
+ data.tar.gz: 680a766582636eaf7b826bc8b7eccee35967b66c99ac98804cd9da1a5d039d6c247c740d67eb29e50cd7956a52419a4a75095fbefb9d31ac6f77bce001917113
data/CHANGELOG.md CHANGED
@@ -3,10 +3,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3
3
 
4
4
  This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachangelog.com/)
5
5
 
6
- ## 0.3.0 - 2018-07-13
7
- ### Added
6
+ ## [0.4.0] - 2018-07-13
7
+ - Added handling for client environments set as array of values
8
+ - Added schemas so graphite metric names can be parsed into dimensional metrics
9
+ - Added debug option
10
+
11
+ ## [0.3.0] - 2018-07-13
8
12
  - Better logging
9
13
 
10
- ## 0.1.0 - 2018-07-12
11
- ### Added
14
+ ## [0.1.0] - 2018-07-12
12
15
  - initial release
data/README.md CHANGED
@@ -1,113 +1,47 @@
1
- Outlyer Sensu Handler Integration
2
- =================================
1
+ # Sensu Outlyer Handler Plugin
3
2
 
4
- == Description ==
3
+ This readme includes development instructions for building, packaging and testing the Sensu plugin in Ruby.
5
4
 
6
- This integration provides instructions to install and use the Outlyer Sensu Handler Integration
7
- to push all your check event metrics and check status's from Sensu checks into Outlyer. It supports
8
- both Nagios checks and Graphite Metric check outputs from Sensu.
5
+ ## Packaging and Publishing
9
6
 
10
- Once installed and enabled you should see all your check metrics and check status's in Outlyer to
11
- use on custom dashboards and alerts.
12
-
13
- == Metrics Collected ==
14
-
15
- The Sensu integration will push any metric collected by your Sensu checks with metric
16
- names namespaced to the check name as follows:
7
+ First build the gem:
17
8
 
18
9
  ```
19
- <check_name>.<metric_name>
10
+ gem build sensu-plugins-outlyer.gemspec
20
11
  ```
21
12
 
22
- In addition the status code of each check is sent as the `service.status` metric
23
- to Outlyer with the label `service: <check_name>`. You will be able to use this
24
- metric to configure dashboad status widgets and service check alerts in Outlyer.
25
-
26
- For Nagios checks, the timestamp of when the check was executed is used for all the
27
- metric data points. For Graphite metric checks the timestamp of each metric data
28
- point is used, apart from the service.status which uses the check execution time
29
- like Nagios checks.
30
-
31
- == Installation ==
32
-
33
- Firstly install the Outlyer Sensu plugin on your Sensu machine(s) using the following
34
- command:
13
+ Then publish the sensu-plugins-outlyer gem to the Outlyer account on the RubyGems server
14
+ using the following command:
35
15
 
36
16
  ```bash
37
- sensu-install -p outlyer
17
+ gem push sensu-plugins-outlyer-<version_number>.gem
38
18
  ```
39
19
 
40
- Next, we’ll enable the handlers by adding it to `/etc/sensu/conf.d/outlyer-handlers.json`:
41
-
42
- ```json
43
- {
44
- "handlers": {
45
- "outlyer-graphite": {
46
- "type": "pipe",
47
- "command": "/opt/sensu/embedded/bin/outlyer-metrics.rb"
48
- },
49
- "outlyer-nagios": {
50
- "type": "pipe",
51
- "command": "/opt/sensu/embedded/bin/outlyer-metrics.rb -f nagios"
52
- }
53
- }
54
- }
55
- ```
56
-
57
- Two handlers have been configured, one for your Nagios checks `outlyer-nagios` and
58
- one for your Graphite metric checks `outlyer-graphite`. You can refer to these handlers
59
- in your check configurations to ensure they are handled by the Outlyer handler with
60
- the correct check output parsing.
61
-
62
- You can also add the optional `-t` parameter to the handler command to change the default
63
- timeout for Outlyer API requests from 5 seconds. This is useful if you notice the handler
64
- timing out as your checks send more metrics creating larger payloads.
20
+ ## Testing
65
21
 
66
- Finally add your Outlyer API configuration at `/etc/sensu/conf.d/outlyer.json`:
22
+ First copy the example config file under `test/test-config.json` to a path outside your
23
+ project and set the account and your API key in the new file. Then set the path to your
24
+ handler configuration file with your API details as an environment variable:
67
25
 
68
- ```json
69
- {
70
- "outlyer": {
71
- "api_key": "<API_KEY>",
72
- "account": "<ACCOUNT_NAME>"
73
- }
74
- }
26
+ ```bash
27
+ export SENSU_CONFIG_FILES='/Users/dgildeh/Development/Outlyer/sensu-config.json'
75
28
  ```
76
29
 
77
- Where:
78
-
79
- * **api_key**: Your user's API key generated under user settings
80
- * **account**: The unique account name of the account you want to push metrics too (you will see this in the URL when using the app)
81
-
82
- Finally restart your Sensu server so the new handler is enabled:
30
+ Then you can use the following command to test the handler with example Nagios
31
+ output data using the following command:
83
32
 
84
33
  ```bash
85
- systemctl restart sensu-{server,api}
34
+ cat ./test/test-nagios-data.json | ruby ./bin/outlyer-metrics.rb -f nagios
86
35
  ```
87
-
88
- You will need to add the handler to all the checks you want metrics sent to Outlyer for by adding
89
- `outlyer-nagios` or `outlyer-graphite` to your check handlers configuration as appropriate. Once
90
- your checks have been configured to use the Outlyer handler you should start seeing all your
91
- metrics from your configured Sensu checks appearing in Outlyer shortly afterwards.
92
-
93
- Also ensure any Sensu checks that send metrics have type `metric` otherwise the output will not
94
- be processed everytime the check runs.
95
-
96
- Please read the Metrics Collected section of this guide to understand how your check metrics
97
- are sent to Outlyer.
98
-
99
- ## Troubleshooting
100
-
101
- If you don't see metrics appearing in Outlyer after enabling the handlers and checks
102
- you should use the following command to view the Sensu server logs to see if any
103
- errors are being output by the hander:
36
+ For Graphite metric test:
104
37
 
105
38
  ```bash
106
- tail -f /var/log/sensu/sensu-server.log | grep outlyer
39
+ cat ./test/test-graphite-data.json | ruby ./bin/outlyer-metrics.rb
107
40
  ```
108
41
 
109
- == Changelog ==
42
+ Note you will have to override the check timestamp to the current time so your
43
+ metrics will appear in Outlyer using the test above:
110
44
 
111
- |Version|Release Date|Description |
112
- |-------|------------|-----------------------------------------------------|
113
- |1.0 |12-Jul-2018 |Initial version of the Outlyer Sensu Handler. |
45
+ ```ruby
46
+ timestamp = Time.now.to_i * 1000
47
+ ```
@@ -51,6 +51,12 @@ class OutlyerMetrics < Sensu::Handler
51
51
  short: '-t TIMEOUT',
52
52
  long: '--timeout TIMEOUT',
53
53
  default: '5'
54
+
55
+ option :debug,
56
+ description: 'Enable debug for more verbose output',
57
+ short: '-d DEBUG',
58
+ long: '--debug DEBUG',
59
+ default: 'false'
54
60
 
55
61
  # Override filters from Sensu::Handler.
56
62
  # They are not appropriate for metric handlers
@@ -62,16 +68,22 @@ class OutlyerMetrics < Sensu::Handler
62
68
  #
63
69
  def handle
64
70
 
71
+ @debug = false
72
+ if config[:debug] == 'true'
73
+ @debug = true
74
+ end
75
+
65
76
  output = @event['check']['output']
66
77
  check_status = @event['check']['status'].to_f
67
78
  @check_name = @event['check']['name']
68
- @host = @event['client']['name']
79
+ @host = @event['client']['name'].strip.to_s
69
80
  @environment = @event['client']['environment']
81
+ if @environment.kind_of?(Array)
82
+ # environment can be an array, so merge values with hyphens
83
+ @environment = @environment.join("-")
84
+ end
70
85
  timestamp = @event['check']['executed'].to_i * 1000
71
86
 
72
- # Uncomment this line for local testing outside Sensu
73
- #@settings = { "outlyer" => {"account" => "", "api_key" => ""}}
74
-
75
87
  # Parse output for metric data
76
88
  metrics = if config[:output_format] == 'nagios' then
77
89
  parse_nagios_output(output, timestamp)
@@ -80,7 +92,7 @@ class OutlyerMetrics < Sensu::Handler
80
92
  end
81
93
 
82
94
  # Add a service status metric
83
- metrics.push(create_datapoint('service.status', check_status, timestamp, {service: "sensu.#{@check_name}"}))
95
+ metrics.push(create_datapoint('service.status', check_status, timestamp, {service: "sensu.#{sanitize_value(@check_name)}"}))
84
96
  # Post metrics to Outlyer
85
97
  push_metrics(metrics)
86
98
  end
@@ -127,10 +139,11 @@ class OutlyerMetrics < Sensu::Handler
127
139
  begin
128
140
  name = metric_parts[0]
129
141
  value = metric_parts[1].split(";")[0].to_f
130
- data.push(create_datapoint(@check_name + '.' + name, value, timestamp))
142
+ labels = {service: "sensu.#{sanitize_value(@check_name)}"}
143
+ data.push(create_datapoint(name, value, timestamp, labels))
131
144
  rescue => error
132
145
  # Raised when any metrics could not be parsed
133
- puts "[OutlyerHandler] The Nagios metric '#{metric}' could not be parsed: #{error.message}"
146
+ puts "The Nagios metric '#{metric}' could not be parsed: #{error.message}"
134
147
  end
135
148
  end
136
149
  data
@@ -145,17 +158,71 @@ class OutlyerMetrics < Sensu::Handler
145
158
  #
146
159
  def parse_graphite_output(output)
147
160
  data = []
161
+
162
+ # Get Scheme to parse Graphite metrics if exists
163
+ scheme = nil
164
+ if settings['outlyer'].key?('schemes') && settings['outlyer']['schemes'] != nil
165
+ if settings['outlyer']['schemes'].key?(@check_name)
166
+ scheme = settings['outlyer']['schemes'][@check_name]
167
+ else
168
+ # Get default scheme if check specific scheme not defined
169
+ scheme = settings['outlyer']['schemes']['default']
170
+ end
171
+ end
172
+
173
+ # Parse the metric on each line in graphite format
148
174
  output.split("\n").each do |metric|
149
175
  m = metric.split
150
176
  next unless m.count == 3
151
- name = m[0].split('.', 2)[1]
177
+ # Get name and extract labels - we remove hostname if in metric name
178
+ # as it contains dots and breaks parsing
179
+ metric_parts = m[0].gsub("#{@host}", 'host').split('.')
180
+ labels = {service: "sensu.#{sanitize_value(@check_name)}"}
181
+ if scheme
182
+ metric_name = metric_parts.last(scheme['metric_name_length'].to_i).join('.')
183
+ # Get dimensions from metric name
184
+ schema_parts = scheme['scheme'].split('.')
185
+ if (metric_parts.length - scheme['metric_name_length'].to_i) != schema_parts.length
186
+ puts "Schema Parsing Error: metric parts (#{metric_parts.length - scheme['metric_name_length'].to_i}) is not same length as schema (#{schema_parts.length})"
187
+ puts "Example metric: m[0]"
188
+ return []
189
+ end
190
+ schema_parts.each_with_index do |val,index|
191
+ if val != 'host' && val != 'hostname' && val != 'name'
192
+ labels.merge!(Hash[sanitize_value(val, 40),sanitize_value(metric_parts[index])])
193
+ end
194
+ end
195
+ else
196
+ metric_name = sanitize_value(metric_parts.join('.'))
197
+ end
152
198
  value = m[1].to_f
153
- time = m[2].to_i * 1000
154
- point = create_datapoint(@check_name + '.' + name, value, time)
199
+ time = Time.now.to_i * 1000
200
+ point = create_datapoint(metric_name, value, time, labels)
155
201
  data.push(point)
156
202
  end
157
203
  data
158
204
  end
205
+
206
+ # Ensures all label values conform to Outlyer's data format requirements:
207
+ #
208
+ # * values can only contain the characters `-._A-Za-z0-9`, other characters
209
+ # will be replaced by underscore including spaces
210
+ # * values can only be 80 characters or less
211
+ #
212
+ # @param value [String] The value to sanitize
213
+ # @param max_length [Integer] The maximum length of string, default 80
214
+ #
215
+ def sanitize_value(value, max_length = 80)
216
+ if !value
217
+ return ''
218
+ end
219
+ value = value.gsub(/[^-._A-Za-z0-9]/i, '_').downcase
220
+ if value.length > max_length
221
+ puts "Warning: value '#{value}' is greater than 80 characters and will be truncated to last 80 characters"
222
+ value = value.split(//).last(max_length).join
223
+ end
224
+ value
225
+ end
159
226
 
160
227
  # Post check metrics to Outlyer's Series API:
161
228
  #
@@ -166,7 +233,7 @@ class OutlyerMetrics < Sensu::Handler
166
233
  def push_metrics(datapoints)
167
234
  Timeout.timeout(config[:api_timeout].to_i) do
168
235
  uri = URI.parse("https://api2.outlyer.com/v2/accounts/#{settings['outlyer']['account']}/series")
169
-
236
+
170
237
  # Create the HTTP objects
171
238
  http = Net::HTTP.new(uri.host, uri.port)
172
239
  http.use_ssl = true
@@ -176,13 +243,17 @@ class OutlyerMetrics < Sensu::Handler
176
243
  request.add_field("accept", "application/json")
177
244
  request.add_field("Content-Type", "application/json")
178
245
  request.body = {samples: datapoints}.to_json
179
-
246
+
247
+ if @debug
248
+ puts "DEBUG: Outlyer API Payload: #{request.body}"
249
+ end
250
+
180
251
  # Send the request
181
252
  response = http.request(request)
182
253
  if response.code.to_i != 200
183
- puts "[OutlyerHandler] Outlyer API Error -- API responded with status code #{response.code}"
184
- puts "[OutlyerHandler] Outlyer API Response: #{response.body}"
185
- puts "[OutlyerHandler] Outlyer API Request Body: #{request.body}"
254
+ puts "Outlyer API Error -- API responded with status code #{response.code}"
255
+ puts "Outlyer API Response: #{response.body}"
256
+ puts "Outlyer API Request Body: #{request.body}"
186
257
  return
187
258
  end
188
259
  puts "Successfully pushed #{datapoints.length} metrics to Outlyer"
@@ -190,9 +261,9 @@ class OutlyerMetrics < Sensu::Handler
190
261
  # Raised when any metrics could not be sent
191
262
  #
192
263
  rescue Timeout::Error
193
- puts '[OutlyerHandler] Outlyer API Error -- timed out while sending metrics'
264
+ puts 'Outlyer API Error -- timed out while sending metrics'
194
265
  rescue => error
195
- puts "[OutlyerHandler] Outlyer API Error -- failed to send metrics: #{error.message}"
266
+ puts "Outlyer API Error -- failed to send metrics: #{error.message}"
196
267
  puts " #{error.backtrace.join("\n\t")}"
197
268
  end
198
269
  end
@@ -1,7 +1,7 @@
1
1
  module SensuPluginsOutlyer
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 3
4
+ MINOR = 4
5
5
  PATCH = 0
6
6
 
7
7
  VER_STRING = [MAJOR, MINOR, PATCH].compact.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sensu-plugins-outlyer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dataloop Software, INC. Trading as Outlyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-13 00:00:00.000000000 Z
11
+ date: 2018-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sensu-plugin