sensu-plugins-stackstorm 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []