sensu-plugins-stackstorm 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be14a142d09aa3425b4a138764b62fbd2f3fa9ee
4
+ data.tar.gz: 3cbc5eea735cde517d23f385bc374aae8530a097
5
+ SHA512:
6
+ metadata.gz: 1a3b5e462bd30c6ab63c4a8c33e9002f28c072badf755e5ef7115ed13198bff9ad73b27229ecbc4e3eedaf90d6fd35d0cf59aa55c5dff8b2293dd87aed5670db
7
+ data.tar.gz: 64ad6a92f02447001988d552c97337f2ee0821bc2815c4a32ff34aea63b6859f167c91d9467cc93df870cec67f9f473c098c8756c2da5eee8cedbf9ab9fe7004
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.sw[p-z]
2
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,34 @@
1
+ language: ruby
2
+ cache:
3
+ - bundler
4
+ install:
5
+ - bundle install
6
+ rvm:
7
+ - 2.0
8
+ - 2.1
9
+ - 2.2
10
+ - 2.3.0
11
+ - 2.4.1
12
+ notifications:
13
+ email:
14
+ recipients:
15
+ - user.localhost2000@gmail.com
16
+ on_success: change
17
+ on_failure: always
18
+ script:
19
+ - bundle exec rake default
20
+ - gem build sensu-plugins-stackstorm.gemspec
21
+ - gem install sensu-plugins-stackstorm-*.gem
22
+ deploy:
23
+ provider: rubygems
24
+ api_key:
25
+ secure: m6eh9IW8L2RCDzfFcnVNuWyr74ywhw+G+4WWwvTTUb7kp5cPu1dkqSeyVM8UobwKuFDwiJTh5rsGVfa2gTnPkaj7jaAWOVDAJcjs8q+oYxMob+dpe++eu3N1adbsqZC27wNH2AlfmIiuEohTHNTMYv6CTiIkr6OVkZCLeD1lLxx9+OyhNaxkb53hmPPgvADmvITK1ZKKyAKb43rCGrKV1/nQDFu+aL/+CmqlH0JLw7vYrx3uVFOmjoVmPWM5VWS1BO2xOZIJ2leInql85C4DK4K3FoqIDhAg+d1o5RqMEz9DvfLHhl2f2EWgHxGk2PyolVUu7JxIoPT1WgCQnKjZ4vOZZBpsdMLzjvnFJBHrFIp0wUTAUgDxHzCA16BKLEoZg0IMcPh+aVPd3jdVJg+RsUuhCr+5wZEsu8LB/LfKiLhSdaM70sjM6CsZXyfhrLFN809f32vGm/fFc3FOKy/ZHEDaVG2jIspcg2pwDwzaQ8wPYwsgp9q+RDpFF49zyk+5f9FjEyftcUwaZhzDb1WIvzQCKtR2dBNgooUZJKrCicyc/L409P1Q1XMeV7BFew+fXChteh4upbzYiqp1yile1EO3QDIDmHpPALh2CbWRwyCmpbn0y8ZfAE4YDs0Bd1V1rJqdsuUFJlEDw7coKrR9uPWzB66om8nlz3KQGfyva3I=
26
+ gem: sensu-plugins-stackstorm
27
+ on:
28
+ tags: true
29
+ all_branches: true
30
+ rvm: 2.4.1
31
+ repo: sensu-plugins/sensu-plugins-stackstorm
32
+ addons:
33
+ code_climate:
34
+ repo_token:
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Sensu-Plugins-StackStorm
2
+
3
+ # Functionality
4
+
5
+ ## Files
6
+
7
+ * st2_handler.py
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,380 @@
1
+ #!/usr/bin/env python
2
+
3
+ import argparse
4
+ import base64
5
+ import httplib
6
+ try:
7
+ import simplejson as json
8
+ except ImportError:
9
+ import json
10
+ import os
11
+ import sys
12
+ import traceback
13
+ from urlparse import urljoin
14
+
15
+ try:
16
+ import requests
17
+ requests.packages.urllib3.disable_warnings()
18
+ except ImportError:
19
+ raise ImportError('Missing dependency "requests". \
20
+ Do ``pip install requests``.')
21
+
22
+ try:
23
+ import yaml
24
+ except ImportError:
25
+ raise ImportError('Missing dependency "pyyaml". \
26
+ Do ``pip install pyyaml``.')
27
+
28
+ # ST2 configuration
29
+
30
+ ST2_API_BASE_URL = None # 'https://localhost/api/v1/'
31
+ ST2_AUTH_BASE_URL = None # 'https://localhost/auth/v1/'
32
+ ST2_USERNAME = None
33
+ ST2_PASSWORD = None
34
+ ST2_API_KEY = None
35
+ ST2_AUTH_TOKEN = None
36
+ ST2_SSL_VERIFY = False
37
+
38
+ ST2_AUTH_PATH = 'tokens'
39
+ ST2_WEBHOOKS_PATH = 'webhooks/st2'
40
+ ST2_TRIGGERS_PATH = 'triggertypes'
41
+ ST2_TRIGGERTYPE_PACK = 'sensu'
42
+ ST2_TRIGGERTYPE_NAME = 'event_handler'
43
+ ST2_TRIGGERTYPE_REF = '.'.join([ST2_TRIGGERTYPE_PACK, ST2_TRIGGERTYPE_NAME])
44
+
45
+ # Sensu configuration
46
+
47
+ SENSU_HOST = 'localhost'
48
+ SENSU_PORT = 4567
49
+ SENSU_USER = ''
50
+ SENSU_PASS = ''
51
+
52
+ REGISTERED_WITH_ST2 = False
53
+ UNAUTHED = False
54
+ IS_API_KEY_AUTH = False
55
+
56
+ OK_CODES = [httplib.OK, httplib.CREATED, httplib.ACCEPTED, httplib.CONFLICT]
57
+ UNREACHABLE_CODES = [httplib.NOT_FOUND]
58
+
59
+ TOKEN_AUTH_HEADER = 'X-Auth-Token'
60
+ API_KEY_AUTH_HEADER = 'St2-Api-Key'
61
+
62
+
63
+ def _get_sensu_request_headers():
64
+ b64auth = base64.b64encode(
65
+ "%s:%s" %
66
+ (SENSU_USER, SENSU_PASS))
67
+ auth_header = "BASIC %s" % b64auth
68
+ content_header = "application/json"
69
+ return {"Authorization": auth_header, "Content-Type": content_header}
70
+
71
+
72
+ def _check_stash(client, check, verbose=False):
73
+ sensu_api = "http://%s:%i" % (SENSU_HOST, SENSU_PORT)
74
+ endpoints = [
75
+ "silence/%s" % client,
76
+ "silence/%s/%s" % (client, check),
77
+ "silence/all/%s" % check]
78
+
79
+ for endpoint in endpoints:
80
+ url = "%s/stashes/%s" % (sensu_api, endpoint)
81
+
82
+ if verbose:
83
+ print('Getting sensu stash info from URL: %s' % url)
84
+
85
+ try:
86
+ response = requests.get(url, headers=_get_sensu_request_headers())
87
+ except requests.exceptions.ConnectionError:
88
+ traceback.print_exc(limit=20)
89
+ msg = 'Couldn\'t connect to sensu to get stash info. Is sensu running on %s:%s?' % (
90
+ SENSU_HOST, SENSU_PORT
91
+ )
92
+ raise Exception(msg)
93
+
94
+ if verbose:
95
+ print('Sensu response code: %s.' % response.status_code)
96
+
97
+ if response.status_code == 200:
98
+ print("Check or client is stashed")
99
+ sys.exit(0)
100
+
101
+
102
+ def _get_st2_request_headers():
103
+ headers = {}
104
+
105
+ if not UNAUTHED:
106
+ if IS_API_KEY_AUTH:
107
+ headers[API_KEY_AUTH_HEADER] = ST2_API_KEY
108
+ else:
109
+ if ST2_AUTH_TOKEN:
110
+ headers[TOKEN_AUTH_HEADER] = ST2_AUTH_TOKEN
111
+ else:
112
+ pass
113
+
114
+ return headers
115
+
116
+
117
+ def _create_trigger_type(verbose=False):
118
+ try:
119
+ url = _get_st2_triggers_base_url()
120
+ payload = {
121
+ 'name': ST2_TRIGGERTYPE_NAME,
122
+ 'pack': ST2_TRIGGERTYPE_PACK,
123
+ 'description': 'Trigger type for sensu event handler.'
124
+ }
125
+
126
+ headers = _get_st2_request_headers()
127
+ headers['Content-Type'] = 'application/json; charset=utf-8'
128
+
129
+ if verbose:
130
+ print('POST to URL %s for registering trigger. Body = %s, headers = %s.' %
131
+ (url, payload, headers))
132
+ post_resp = requests.post(url, data=json.dumps(payload),
133
+ headers=headers, verify=ST2_SSL_VERIFY)
134
+ except:
135
+ traceback.print_exc(limit=20)
136
+ raise Exception('Unable to connect to st2 endpoint %s.' % url)
137
+ else:
138
+ status = post_resp.status_code
139
+ if status in UNREACHABLE_CODES:
140
+ msg = 'Got response %s. Invalid triggers endpoint %s. Check configuration!' % (
141
+ status,
142
+ url
143
+ )
144
+ raise Exception(msg)
145
+
146
+ if status not in OK_CODES:
147
+ msg = 'Failed to register trigger type %s.%s with st2. HTTP_CODE: %s' % (
148
+ ST2_TRIGGERTYPE_PACK, ST2_TRIGGERTYPE_NAME, status
149
+ )
150
+ raise Exception(msg)
151
+ else:
152
+ print('Registered trigger type with st2.')
153
+
154
+
155
+ def _get_auth_url():
156
+ return urljoin(ST2_AUTH_BASE_URL, ST2_AUTH_PATH)
157
+
158
+
159
+ def _get_auth_token(verbose=False):
160
+ auth_url = _get_auth_url()
161
+
162
+ if verbose:
163
+ print('Will POST to URL %s to get auth token.' % auth_url)
164
+
165
+ try:
166
+ resp = requests.post(auth_url, json.dumps({'ttl': 5 * 60}),
167
+ auth=(ST2_USERNAME, ST2_PASSWORD), verify=ST2_SSL_VERIFY)
168
+ except:
169
+ traceback.print_exc(limit=20)
170
+ raise Exception('Unable to connect to st2 endpoint %s.' % auth_url)
171
+ else:
172
+ if resp.status_code in UNREACHABLE_CODES:
173
+ msg = 'Got response %s. Invalid auth endpoint %s. Check configuration!' % (
174
+ resp.status_code,
175
+ auth_url
176
+ )
177
+ raise Exception(msg)
178
+ if resp.status_code not in OK_CODES:
179
+ msg = 'Cannot get a valid auth token from %s. HTTP_CODE: %s' % (
180
+ auth_url,
181
+ resp.status_code
182
+ )
183
+ raise Exception(msg)
184
+ return resp.json()['token']
185
+
186
+
187
+ def _register_trigger_with_st2(verbose=False):
188
+ triggers_url = _get_st2_triggers_url()
189
+
190
+ try:
191
+ headers = _get_st2_request_headers()
192
+ if verbose:
193
+ print('Will GET from URL %s for detecting trigger %s.' % (
194
+ triggers_url, ST2_TRIGGERTYPE_REF))
195
+ print('Request headers: %s' % headers)
196
+ get_resp = requests.get(triggers_url, headers=headers, verify=ST2_SSL_VERIFY)
197
+
198
+ if get_resp.status_code != httplib.OK:
199
+ _create_trigger_type(verbose=verbose)
200
+ else:
201
+ body = json.loads(get_resp.text)
202
+ if len(body) == 0:
203
+ _create_trigger_type(verbose=verbose)
204
+ except:
205
+ traceback.print_exc(limit=20)
206
+ raise Exception('Unable to connect to st2 endpoint %s.' % triggers_url)
207
+ else:
208
+ if verbose:
209
+ print('Successfully registered trigger %s with st2.' % ST2_TRIGGERTYPE_REF)
210
+
211
+
212
+ def _get_st2_triggers_base_url():
213
+ url = urljoin(ST2_API_BASE_URL, ST2_TRIGGERS_PATH)
214
+ return url
215
+
216
+
217
+ def _get_st2_triggers_url():
218
+ url = urljoin(_get_st2_triggers_base_url() + '/', ST2_TRIGGERTYPE_REF)
219
+ return url
220
+
221
+
222
+ def _get_st2_webhooks_url():
223
+ url = urljoin(ST2_API_BASE_URL, ST2_WEBHOOKS_PATH)
224
+ return url
225
+
226
+
227
+ def _post_webhook(url, body, verbose=False):
228
+ headers = _get_st2_request_headers()
229
+ headers['X-ST2-Integration'] = 'sensu.'
230
+ headers['St2-Trace-Tag'] = body['payload']['id']
231
+ headers['Content-Type'] = 'application/json; charset=utf-8'
232
+
233
+ try:
234
+ if verbose:
235
+ print('Webhook POST: url: %s, headers: %s, body: %s\n' % (url, headers, body))
236
+ r = requests.post(url, data=json.dumps(body), headers=headers, verify=False)
237
+ except:
238
+ raise Exception('Cannot connect to st2 endpoint %s.' % url)
239
+ else:
240
+ status = r.status_code
241
+
242
+ if status in UNREACHABLE_CODES:
243
+ msg = 'Webhook URL %s does not exist. Check StackStorm installation!' % (url)
244
+ raise Exception(msg)
245
+
246
+ if status not in OK_CODES:
247
+ sys.stderr.write('Failed posting sensu event to st2. HTTP_CODE: \
248
+ %d\n' % status)
249
+ else:
250
+ sys.stdout.write('Sent sensu event to st2. HTTP_CODE: \
251
+ %d\n' % status)
252
+
253
+
254
+ def _post_event_to_st2(payload, verbose=False):
255
+ body = {}
256
+ body['trigger'] = ST2_TRIGGERTYPE_REF
257
+
258
+ try:
259
+ body['payload'] = json.loads(payload)
260
+ except:
261
+ print('Invalid JSON payload %s.' % payload)
262
+ sys.exit(3)
263
+
264
+ try:
265
+ client = body['payload']['client']['name']
266
+ check = body['payload']['check']['name']
267
+ except KeyError:
268
+ print('Invalid payload spec %s.' % payload)
269
+
270
+ if not _check_stash(client, check, verbose=verbose):
271
+ try:
272
+ _post_webhook(url=_get_st2_webhooks_url(), body=body, verbose=verbose)
273
+ return True
274
+ except:
275
+ traceback.print_exc(limit=20)
276
+ print('Cannot send event to st2.')
277
+ sys.exit(4)
278
+ return False
279
+
280
+
281
+ def _register_with_st2(verbose=False):
282
+ global REGISTERED_WITH_ST2
283
+ try:
284
+ if not REGISTERED_WITH_ST2:
285
+ if verbose:
286
+ print('Checking if trigger %s registered with st2.' % ST2_TRIGGERTYPE_REF)
287
+ _register_trigger_with_st2(verbose=verbose)
288
+ REGISTERED_WITH_ST2 = True
289
+ except:
290
+ traceback.print_exc(limit=20)
291
+ sys.stderr.write(
292
+ 'Failed registering with st2. Won\'t post event.\n')
293
+ sys.exit(2)
294
+
295
+
296
+ def _set_config_opts(config_file, verbose=False, unauthed=False, ssl_verify=False):
297
+ global ST2_USERNAME
298
+ global ST2_PASSWORD
299
+ global ST2_API_KEY
300
+ global ST2_AUTH_TOKEN
301
+ global ST2_API_BASE_URL
302
+ global ST2_API_BASE_URL
303
+ global ST2_AUTH_BASE_URL
304
+ global ST2_SSL_VERIFY
305
+ global SENSU_HOST
306
+ global SENSU_PORT
307
+ global SENSU_USER
308
+ global SENSU_PASS
309
+ global UNAUTHED
310
+ global IS_API_KEY_AUTH
311
+
312
+ UNAUTHED = unauthed
313
+ ST2_SSL_VERIFY = ssl_verify
314
+
315
+ if not os.path.exists(config_file):
316
+ print('Configuration file %s not found. Exiting!!!' % config_file)
317
+ sys.exit(1)
318
+
319
+ with open(config_file) as f:
320
+ config = yaml.safe_load(f)
321
+
322
+ if verbose:
323
+ print('Contents of config file: %s' % config)
324
+
325
+ ST2_USERNAME = config['st2_username']
326
+ ST2_PASSWORD = config['st2_password']
327
+ ST2_API_KEY = config['st2_api_key']
328
+ ST2_API_BASE_URL = config['st2_api_base_url']
329
+ if not ST2_API_BASE_URL.endswith('/'):
330
+ ST2_API_BASE_URL += '/'
331
+ ST2_AUTH_BASE_URL = config['st2_auth_base_url']
332
+ if not ST2_AUTH_BASE_URL.endswith('/'):
333
+ ST2_AUTH_BASE_URL += '/'
334
+ SENSU_HOST = config.get('sensu_host', 'localhost')
335
+ SENSU_PORT = config.get('sensu_port', '4567')
336
+ SENSU_USER = config.get('sensu_user', None)
337
+ SENSU_PASS = config.get('sensu_pass', None)
338
+
339
+ if ST2_API_KEY:
340
+ IS_API_KEY_AUTH = True
341
+
342
+ if verbose:
343
+ print('Unauthed? : %s' % UNAUTHED)
344
+ print('API key auth?: %s' % IS_API_KEY_AUTH)
345
+ print('SSL_VERIFY? : %s' % ST2_SSL_VERIFY)
346
+
347
+ if not UNAUTHED and not IS_API_KEY_AUTH:
348
+ try:
349
+ if not ST2_AUTH_TOKEN:
350
+ if verbose:
351
+ print('No auth token found. Let\'s get one from StackStorm!')
352
+ ST2_AUTH_TOKEN = _get_auth_token(verbose=verbose)
353
+ except:
354
+ traceback.print_exc(limit=20)
355
+ print('Unable to negotiate an auth token. Exiting!')
356
+ sys.exit(1)
357
+
358
+
359
+ def main(config_file, payload, verbose=False, unauthed=False, ssl_verify=False):
360
+ _set_config_opts(config_file=config_file, unauthed=unauthed, verbose=verbose,
361
+ ssl_verify=ssl_verify)
362
+ _register_with_st2(verbose=verbose)
363
+ _post_event_to_st2(payload, verbose=verbose)
364
+
365
+
366
+ if __name__ == '__main__':
367
+ parser = argparse.ArgumentParser(description='StackStorm sensu event handler.')
368
+ parser.add_argument('config_path',
369
+ help='Exchange to listen on')
370
+ parser.add_argument('--verbose', '-v', required=False, action='store_true',
371
+ help='Verbose mode.')
372
+ parser.add_argument('--unauthed', '-u', required=False, action='store_true',
373
+ help='Allow to post to unauthed st2. E.g. when auth is disabled ' +
374
+ 'server side.')
375
+ parser.add_argument('--ssl-verify', '-k', required=False, action='store_true',
376
+ help='Turn on SSL verification for st2 APIs.')
377
+ args = parser.parse_args()
378
+ payload = sys.stdin.read().strip()
379
+ main(config_file=args.config_path, payload=payload, verbose=args.verbose,
380
+ unauthed=args.unauthed, ssl_verify=args.ssl_verify)
@@ -0,0 +1,14 @@
1
+ ---
2
+ # st2 credentials. you can either use username/password combo or specify an API key.
3
+ st2_username: ""
4
+ st2_password: ""
5
+ st2_api_key: ""
6
+
7
+ # trailing slash mandatory
8
+ st2_api_base_url: "https://localhost/api/v1/"
9
+ st2_auth_base_url: "https://localhost/auth/v1/"
10
+
11
+ sensu_host: localhost
12
+ sensu_port: 4567
13
+ sensu_user: sensu
14
+ sensu_pass: ""
data/lib/.gitkeep ADDED
File without changes
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "sensu-plugins-stackstorm"
5
+ spec.version = '0.0.2'
6
+ spec.authors = ["Hiroyasu OHYAMA"]
7
+ spec.email = ["user.localhost2000@gmail.com"]
8
+
9
+ spec.summary = %q{Sensu plugins for StackStorm}
10
+ spec.description = %q{}
11
+ spec.homepage = "https://github.com/userlocalhost2000/sensu-plugins-stackstorm"
12
+
13
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
14
+ spec.bindir = "bin"
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.license = "Apache-2.0"
17
+
18
+ spec.add_development_dependency 'rspec', '~> 3.6'
19
+ spec.add_development_dependency 'rake', '~> 12.0'
20
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensu-plugins-stackstorm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Hiroyasu OHYAMA
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ description: ''
42
+ email:
43
+ - user.localhost2000@gmail.com
44
+ executables:
45
+ - st2_handler.py
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - ".travis.yml"
51
+ - Gemfile
52
+ - README.md
53
+ - Rakefile
54
+ - bin/st2_handler.py
55
+ - etc/st2_handler.conf
56
+ - lib/.gitkeep
57
+ - sensu-plugins-stackstorm.gemspec
58
+ homepage: https://github.com/userlocalhost2000/sensu-plugins-stackstorm
59
+ licenses:
60
+ - Apache-2.0
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.6.11
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Sensu plugins for StackStorm
82
+ test_files: []