sensu-plugins-outlyer 0.3.0 → 0.4.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
  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