Completed
Pull Request — master (#502)
by
unknown
03:41
created

_get_auth_token()   B

Complexity

Conditions 6

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
c 1
b 0
f 0
dl 0
loc 25
rs 7.5384
1
#!/usr/bin/env python
2
3
import httplib
4
try:
5
    import simplejson as json
6
except ImportError:
7
    import json
8
import os
9
import sys
10
import traceback
11
from urlparse import urljoin
12
13
try:
14
    import requests
15
except ImportError:
16
    raise ImportError('Missing dependency requests. \
17
                      Do ``pip install requests``.')
18
19
try:
20
    import yaml
21
except ImportError:
22
    raise ImportError('Missing dependency pyyaml. \
23
                      Do ``pip install pyyaml``.')
24
25
# ST2 configuration
26
27
ST2_CONFIG_FILE = './config.yaml'
28
29
ST2_API_BASE_URL = 'http://localhost/api/v1/'
30
ST2_AUTH_BASE_URL = 'http://localhost/auth/v1/'
31
ST2_USERNAME = None
32
ST2_PASSWORD = None
33
ST2_API_KEY = None
34
ST2_AUTH_TOKEN = None
35
ST2_SSL_VERIFY = False
36
37
ST2_AUTH_PATH = 'tokens'
38
ST2_WEBHOOKS_PATH = 'webhooks/st2'
39
ST2_TRIGGERS_PATH = 'triggertypes'
40
ST2_TRIGGERTYPE_PACK = 'nagios'
41
ST2_TRIGGERTYPE_NAME = 'service-state-change'
42
ST2_TRIGGERTYPE_REF = '.'.join([ST2_TRIGGERTYPE_PACK, ST2_TRIGGERTYPE_NAME])
43
44
STATE_MESSAGE = {
45
    'OK': 'All is well on the Western front.',
46
    'WARNING': 'We gots a warning yo!',
47
    'UNKNOWN': 'It be unknown...',
48
    'CRITICAL': 'Critical!'
49
}
50
51
REGISTERED_WITH_ST2 = False
52
UNAUTHED = False
53
IS_API_KEY_AUTH = False
54
55
OK_CODES = [httplib.OK, httplib.CREATED, httplib.ACCEPTED, httplib.CONFLICT]
56
UNREACHABLE_CODES = [httplib.NOT_FOUND]
57
58
TOKEN_AUTH_HEADER = 'X-Auth-Token'
59
API_KEY_AUTH_HEADER = 'St2-Api-Key'
60
verbose = True
61
62
63
def _create_trigger_type(verbose=False):
64
    try:
65
        url = _get_st2_triggers_url()
66
        payload = {
67
            'name': ST2_TRIGGERTYPE_NAME,
68
            'pack': ST2_TRIGGERTYPE_PACK,
69
            'description': 'Trigger type for nagios event handler.'
70
        }
71
72
        headers = _get_st2_request_headers()
73
        headers['Content-Type'] = 'application/json; charset=utf-8'
74
75
        if verbose:
76
            print('POST to URL %s for registering trigger. Body = %s, '
77
                  'headers = %s.\n' % (url, payload, headers))
78
79
        post_resp = requests.post(url, data=json.dumps(payload),
80
                                  headers=headers,
81
                                  verify=ST2_SSL_VERIFY)
82
    except:
83
        traceback.print_exc(limit=20)
84
        raise Exception('Unable to connect to st2 endpoint %s.' % url)
85
    else:
86
        status = post_resp.status_code
87
        if status in UNREACHABLE_CODES:
88
            msg = 'Got response %s. Invalid triggers endpoint %s.' \
89
                'Check configuration!' % (status, url)
90
            raise Exception(msg)
91
92
        if status not in OK_CODES:
93
            msg = 'Failed to register trigger type %s.%s with st2. ' \
94
                'HTTP_CODE: %s' % (ST2_TRIGGERTYPE_PACK, ST2_TRIGGERTYPE_NAME,
95
                                   status)
96
            raise Exception(msg)
97
        else:
98
            print('Registered trigger type with st2.\n')
99
100
101
def _get_auth_url():
102
    return urljoin(ST2_AUTH_BASE_URL, ST2_AUTH_PATH)
103
104
105
def _get_auth_token(verbose=False):
106
    # global ST2_AUTH_TOKEN
107
    auth_url = _get_auth_url()
108
109
    if verbose:
110
        print('Will POST to URL %s to get auth token.\n' % auth_url)
111
112
    try:
113
        resp = requests.post(auth_url,
114
                            json.dumps({'ttl': 5 * 60}),
115
                            auth=(ST2_USERNAME, ST2_PASSWORD),
116
                            verify=ST2_SSL_VERIFY)
117
    except:
118
        traceback.print_exc(limit=20)
119
        raise Exception('Unable to connect to st2 endpoint %s.\n' % auth_url)
120
    else:
121
        if resp.status_code in UNREACHABLE_CODES:
122
            msg = 'Got response %s. Invalid auth endpoint %s. ' \
123
                'Check configuration!' % (resp.status_code, auth_url)
124
            raise Exception(msg)
125
        if resp.status_code not in OK_CODES:
126
            msg = 'Cannot get a valid auth token from %s. ' \
127
                'HTTP_CODE: %s' % (auth_url, resp.status_code)
128
            raise Exception(msg)
129
        return resp.json()['token']
130
131
132
def _get_st2_request_headers():
133
    headers = {}
134
135
    if not UNAUTHED:
136
        if IS_API_KEY_AUTH:
137
            headers[API_KEY_AUTH_HEADER] = ST2_API_KEY
138
        else:
139
            if ST2_AUTH_TOKEN:
140
                headers[TOKEN_AUTH_HEADER] = ST2_AUTH_TOKEN
141
            else:
142
                pass
143
144
    return headers
145
146
147
def _register_with_st2(verbose=False):
148
    global REGISTERED_WITH_ST2
149
    try:
150
        if not REGISTERED_WITH_ST2:
151
            if verbose:
152
                print('Checking if trigger "%s" registered with st2.'
153
                      % ST2_TRIGGERTYPE_REF)
154
            _register_trigger_with_st2(verbose=verbose)
155
            REGISTERED_WITH_ST2 = True
156
    except:
157
        traceback.print_exc(limit=20)
158
        sys.stderr.write(
159
            'Failed registering with st2. Won\'t post event.\n')
160
        sys.exit(2)
161
162
163
def _register_trigger_with_st2(verbose=False):
164
    url = urljoin(_get_st2_triggers_url(), ST2_TRIGGERTYPE_REF)
165
    sys.stdout.write('GET: %s\n' % url)
166
167
    try:
168
        headers = _get_st2_request_headers()
169
        if verbose:
170
            print('Will GET from URL %s for detecting trigger %s. \n' %
171
                  (url, ST2_TRIGGERTYPE_REF))
172
            print('Request headers: %s\n' % headers)
173
        get_resp = requests.get(url, headers=headers, verify=ST2_SSL_VERIFY)
174
        # else:
175
        #    get_resp = requests.get(url)
176
        if get_resp.status_code != httplib.OK:
177
            _create_trigger_type(verbose=verbose)
178
        else:
179
            body = json.loads(get_resp.text)
180
            if len(body) == 0:
181
                _create_trigger_type(verbose=verbose)
182
    except:
183
        traceback.print_exc(limit=20)
184
        raise Exception('Unable to connect to st2 endpoint %s.\n' % url)
185
    else:
186
        if verbose:
187
            print('Successfully registered trigger %s with st2.\n'
188
                  % ST2_TRIGGERTYPE_REF)
189
190
191
def _get_st2_triggers_url():
192
    url = urljoin(ST2_API_BASE_URL, ST2_TRIGGERS_PATH)
193
    return url
194
195
196
def _get_st2_webhooks_url():
197
    url = urljoin(ST2_API_BASE_URL, ST2_WEBHOOKS_PATH)
198
    return url
199
200
201
def _post_webhook(url, body, verbose=False):
202
    headers = _get_st2_request_headers()
203
    headers['X-ST2-Integration'] = 'nagios.'
204
    headers['St2-Trace-Tag'] = body['payload']['id']
205
    headers['Content-Type'] = 'application/json; charset=utf-8'
206
207
    try:
208
        if verbose:
209
            print('Webhook POST: url: %s, headers: %s, body: %s\n'
210
                  % (url, headers, body))
211
        r = requests.post(url, data=json.dumps(body), headers=headers,
212
                          verify=False)
213
    except:
214
        raise Exception('Cannot connect to st2 endpoint %s.' % url)
215
    else:
216
        status = r.status_code
217
218
        if status in UNREACHABLE_CODES:
219
            msg = 'Webhook URL %s does not exist. Check StackStorm installation!'
220
            raise Exception(msg)
221
222
        if status not in OK_CODES:
223
            sys.stderr.write('Failed posting nagio event to st2. HTTP_CODE: '
224
                             '%d\n' % status)
225
        else:
226
            sys.stdout.write('Sent nagio event to st2. HTTP_CODE: '
227
                             '%d\n' % status)
228
229
230
def _post_event_to_st2(payload, verbose=False):
231
    body = {}
232
    body['trigger'] = ST2_TRIGGERTYPE_REF
233
234
    try:
235
        body['payload'] = json.loads(payload)
236
    except:
237
        print('Invalid JSON payload %s.' % payload)
238
        sys.exit(3)
239
240
    try:
241
        _post_webhook(url=_get_st2_webhooks_url(), body=body, verbose=verbose)
242
        return True
243
    except:
244
        traceback.print_exc(limit=20)
245
        print('Cannot send event to st2.')
246
        # sys.exit(4)
247
        return False
248
249
250
def _get_payload(host, service, event_id, state, state_type, attempt):
251
    payload = {}
252
    payload['host'] = host
253
    payload['service'] = service
254
    payload['event_id'] = event_id
255
    payload['state'] = state
256
    payload['state_type'] = state_type
257
    payload['attempt'] = attempt
258
    payload['msg'] = STATE_MESSAGE.get(state, 'Undefined state.')
259
    return payload
260
261
262
def main(args):
263
    event_id = args[1]
264
    service = args[2]
265
    state = args[3]
266
    state_type = args[4]
267
    attempt = args[5]
268
    host = args[6]
269
    verbose = args[7]
270
271
    payload = _get_payload(host, service, event_id, state, state_type, attempt)
272
    body = {}
273
    body['trigger'] = ST2_TRIGGERTYPE_REF
274
    body['payload'] = payload
275
    _post_event_to_st2(payload, verbose=verbose)
276
277
278
if __name__ == '__main__':
279
    try:
280
        if not os.path.exists(ST2_CONFIG_FILE):
281
            sys.stderr.write('Configuration file not found. Exiting.\n')
282
            sys.exit(1)
283
284
        with open(ST2_CONFIG_FILE) as f:
285
            config = yaml.safe_load(f)
286
            ST2_USERNAME = config['st2_username']
287
            ST2_PASSWORD = config['st2_password']
288
            ST2_API_BASE_URL = config['st2_api_base_url']
289
            ST2_AUTH_BASE_URL = config['st2_auth_base_url']
290
        try:
291
            if not ST2_AUTH_TOKEN:
292
                print('No auth token found. Let\'s get one from StackStorm!')
293
                ST2_AUTH_TOKEN = _get_auth_token(verbose=verbose)
294
        except:
295
            traceback.print_exc(limit=20)
296
            print('Unable to negotiate an auth token. Exiting!')
297
            sys.exit(1)
298
299
        if not REGISTERED_WITH_ST2:
300
            _register_with_st2(verbose=verbose)
301
    except:
302
        sys.stderr.write('Failed registering with st2. Won\'t post event.\n')
303
    else:
304
        main(sys.argv)
305