Completed
Push — master ( 32acac...19ae40 )
by Lakshmi
11:34
created

_set_config_opts()   F

Complexity

Conditions 13

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 13
c 2
b 0
f 0
dl 0
loc 51
rs 2.8149

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like _set_config_opts() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
import argparse
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
    requests.packages.urllib3.disable_warnings()
22
except ImportError:
23
    raise ImportError('Missing dependency pyyaml. \
24
                      Do ``pip install pyyaml``.')
25
26
# ST2 configuration
27
28
ST2_API_BASE_URL = None
29
ST2_AUTH_BASE_URL = None
30
ST2_USERNAME = None
31
ST2_PASSWORD = None
32
ST2_API_KEY = None
33
ST2_AUTH_TOKEN = None
34
ST2_SSL_VERIFY = False
35
36
ST2_AUTH_PATH = 'tokens'
37
ST2_WEBHOOKS_PATH = 'webhooks/st2'
38
ST2_TRIGGERS_PATH = 'triggertypes'
39
ST2_TRIGGERTYPE_PACK = 'nagios'
40
ST2_TRIGGERTYPE_NAME = 'service_state_change'
41
ST2_TRIGGERTYPE_REF = '.'.join([ST2_TRIGGERTYPE_PACK, ST2_TRIGGERTYPE_NAME])
42
43
STATE_MESSAGE = {
44
    'OK': 'All is well on the Western front.',
45
    'WARNING': 'We gots a warning yo!',
46
    'UNKNOWN': 'It be unknown...',
47
    'CRITICAL': 'Critical!'
48
}
49
50
REGISTERED_WITH_ST2 = False
51
UNAUTHED = False
52
IS_API_KEY_AUTH = False
53
54
OK_CODES = [httplib.OK, httplib.CREATED, httplib.ACCEPTED, httplib.CONFLICT]
55
UNREACHABLE_CODES = [httplib.NOT_FOUND]
56
57
TOKEN_AUTH_HEADER = 'X-Auth-Token'
58
API_KEY_AUTH_HEADER = 'St2-Api-Key'
59
60
61
def _create_trigger_type(verbose=False):
62
    try:
63
        url = _get_st2_triggers_base_url()
64
        payload = {
65
            'name': ST2_TRIGGERTYPE_NAME,
66
            'pack': ST2_TRIGGERTYPE_PACK,
67
            'description': 'Trigger type for nagios event handler.'
68
        }
69
70
        headers = _get_st2_request_headers()
71
        headers['Content-Type'] = 'application/json; charset=utf-8'
72
73
        if verbose:
74
            print('POST to URL {0} for registering trigger. Body = {1}, '
75
                  'headers = {2}.\n'.format(url, payload, headers))
76
77
        post_resp = requests.post(url, data=json.dumps(payload),
78
                                  headers=headers,
79
                                  verify=ST2_SSL_VERIFY)
80
    except:
81
        traceback.print_exc(limit=20)
82
        raise Exception('Unable to connect to st2 endpoint {0}.'.format(url))
83
    else:
84
        status = post_resp.status_code
85
        if status in UNREACHABLE_CODES:
86
            msg = 'Got response {0}. Invalid triggers endpoint {1}.' \
87
                'Check configuration!'.format(status, url)
88
            raise Exception(msg)
89
90
        if status not in OK_CODES:
91
            msg = 'Failed to register trigger type {0}.{1} with st2. ' \
92
                'HTTP_CODE: {2}'.format(ST2_TRIGGERTYPE_PACK,
93
                                        ST2_TRIGGERTYPE_NAME, status)
94
            raise Exception(msg)
95
        else:
96
            print('Registered trigger type with st2.\n')
97
98
99
def _get_auth_url():
100
    return urljoin(ST2_AUTH_BASE_URL, ST2_AUTH_PATH)
101
102
103
def _get_auth_token(verbose=False):
104
    auth_url = _get_auth_url()
105
106
    if verbose:
107
        print('Will POST to URL {0} to get auth token.\n'.format(auth_url))
108
109
    try:
110
        resp = requests.post(auth_url, json.dumps({'ttl': 5 * 60}),
111
                             auth=(ST2_USERNAME, ST2_PASSWORD),
112
                             verify=ST2_SSL_VERIFY)
113
    except:
114
        traceback.print_exc(limit=20)
115
        raise Exception('Unable to connect to st2 endpoint {0}.'.
116
                        format(auth_url))
117
    else:
118
        if resp.status_code in UNREACHABLE_CODES:
119
            msg = 'Got response {0}. Invalid auth endpoint {1}. '\
120
                'Check configuration!'.format(resp.status_code, auth_url)
121
            raise Exception(msg)
122
123
        if resp.status_code not in OK_CODES:
124
            msg = 'Cannot get a valid auth token from {0}. '\
125
                'HTTP_CODE: {1}'.format(auth_url, resp.status_code)
126
            raise Exception(msg)
127
128
        return resp.json()['token']
129
130
131
def _get_st2_request_headers():
132
    headers = {}
133
134
    if not UNAUTHED:
135
        if IS_API_KEY_AUTH:
136
            headers[API_KEY_AUTH_HEADER] = ST2_API_KEY
137
        else:
138
            if ST2_AUTH_TOKEN:
139
                headers[TOKEN_AUTH_HEADER] = ST2_AUTH_TOKEN
140
            else:
141
                pass
142
143
    return headers
144
145
146
def _register_with_st2(verbose=False):
147
    global REGISTERED_WITH_ST2
148
    try:
149
        if not REGISTERED_WITH_ST2:
150
            if verbose:
151
                print('Checking if trigger "{0}" registered with st2.'
152
                      .format(ST2_TRIGGERTYPE_REF))
153
            _register_trigger_with_st2(verbose=verbose)
154
            REGISTERED_WITH_ST2 = True
155
    except:
156
        traceback.print_exc(limit=20)
157
        sys.stderr.write(
158
            'Failed registering with st2. Won\'t post event.\n')
159
        sys.exit(2)
160
161
162
def _register_trigger_with_st2(verbose=False):
163
    triggers_url = _get_st2_triggers_url()
164
165
    try:
166
        headers = _get_st2_request_headers()
167
        if verbose:
168
            print('Will GET from URL {0} for detecting trigger {1}.\n'
169
                  .format(triggers_url, ST2_TRIGGERTYPE_REF))
170
            print('Request headers: {0}\n'.format(headers))
171
172
        get_resp = requests.get(triggers_url, headers=headers,
173
                                verify=ST2_SSL_VERIFY)
174
175
        if get_resp.status_code != httplib.OK:
176
            _create_trigger_type(verbose=verbose)
177
        else:
178
            body = json.loads(get_resp.text)
179
            if len(body) == 0:
180
                _create_trigger_type(verbose=verbose)
181
    except:
182
        traceback.print_exc(limit=20)
183
        raise Exception('Unable to connect to st2 endpoint {0}.\n'
184
                        .format(triggers_url))
185
    else:
186
        if verbose:
187
            print('Successfully registered trigger {0} with st2.\n'
188
                  .format(ST2_TRIGGERTYPE_REF))
189
190
191
def _get_st2_triggers_base_url():
192
    url = urljoin(ST2_API_BASE_URL, ST2_TRIGGERS_PATH)
193
    return url
194
195
196
def _get_st2_triggers_url():
197
    url = urljoin(_get_st2_triggers_base_url() + '/', ST2_TRIGGERTYPE_REF)
198
    return url
199
200
201
def _get_st2_webhooks_url():
202
    url = urljoin(ST2_API_BASE_URL, ST2_WEBHOOKS_PATH)
203
    return url
204
205
206
def _post_webhook(url, body, verbose=False):
207
    headers = _get_st2_request_headers()
208
    headers['X-ST2-Integration'] = 'nagios.'
209
    headers['Content-Type'] = 'application/json; charset=utf-8'
210
211
    try:
212
        if verbose:
213
            print('Webhook POST: url: {0}, headers: {1}, body: {2}\n'
214
                  .format(url, headers, body))
215
        r = requests.post(url, data=json.dumps(body), headers=headers,
216
                          verify=False)
217
    except:
218
        traceback.print_exc(10)
219
        raise Exception('Cannot connect to st2 endpoint {0}.'.format(url))
220
    else:
221
        status = r.status_code
222
223
        if status in UNREACHABLE_CODES:
224
            msg = 'Webhook URL {0} does not exist. Check StackStorm '\
225
                'installation!'.format(url)
226
            raise Exception(msg)
227
228
        if status not in OK_CODES:
229
            sys.stderr.write('Failed posting nagio event to st2. HTTP_CODE: '
230
                             '{0}\n'.format(status))
231
        else:
232
            sys.stdout.write('Sent nagios event to st2. HTTP_CODE: '
233
                             '{0}\n'.format(status))
234
235
236
def _post_event_to_st2(payload, verbose=False):
237
    body = {}
238
    body['trigger'] = ST2_TRIGGERTYPE_REF
239
    body['payload'] = payload
240
241
    try:
242
        _post_webhook(url=_get_st2_webhooks_url(), body=body, verbose=verbose)
243
        return True
244
    except:
245
        traceback.print_exc(limit=10)
246
        print('Cannot send event to st2.')
247
        sys.exit(3)
248
249
250
def _set_config_opts(config_file, verbose=False):
251
    global ST2_USERNAME
252
    global ST2_PASSWORD
253
    global ST2_API_KEY
254
    global ST2_AUTH_TOKEN
255
    global ST2_API_BASE_URL
256
    global ST2_AUTH_BASE_URL
257
    global ST2_SSL_VERIFY
258
    global UNAUTHED
259
    global IS_API_KEY_AUTH
260
    if not os.path.exists(config_file):
261
        print('Configuration file "{0}" not found. Exiting!!!'
262
              .format(config_file))
263
        sys.exit(2)
264
265
    with open(config_file) as f:
266
        config = yaml.safe_load(f)
267
268
        if verbose:
269
            print('Contents of config file: {0}'.format(config))
270
271
        ST2_USERNAME = config['st2_username']
272
        ST2_PASSWORD = config['st2_password']
273
        ST2_API_KEY = config.get('st2_api_key', None)
274
        ST2_API_BASE_URL = config['st2_api_base_url']
275
        if not ST2_API_BASE_URL.endswith('/'):
276
            ST2_API_BASE_URL += '/'
277
        ST2_AUTH_BASE_URL = config['st2_auth_base_url']
278
        if not ST2_AUTH_BASE_URL.endswith('/'):
279
            ST2_AUTH_BASE_URL += '/'
280
        UNAUTHED = config['unauthed']
281
        ST2_SSL_VERIFY = config['ssl_verify']
282
283
    if ST2_API_KEY:
284
        IS_API_KEY_AUTH = True
285
286
    if verbose:
287
        print('Unauthed? : {0}\nAPI key auth?: {1}\nSSL Verify? : {2}\n'
288
              .format(UNAUTHED, IS_API_KEY_AUTH, ST2_SSL_VERIFY))
289
290
    if not UNAUTHED and not IS_API_KEY_AUTH:
291
        try:
292
            if not ST2_AUTH_TOKEN:
293
                if verbose:
294
                    print('No auth token found. Let\'s get one from'
295
                          'StackStorm!')
296
                ST2_AUTH_TOKEN = _get_auth_token(verbose=verbose)
297
        except:
298
            traceback.print_exc(limit=20)
299
            print('Unable to negotiate an auth token. Exiting!')
300
            sys.exit(1)
301
302
303
def _from_arg_to_payload(nagios_args):
304
    try:
305
        event_id = nagios_args[0]
306
        service = nagios_args[1]
307
        state = nagios_args[2]
308
        state_id = nagios_args[3]
309
        state_type = nagios_args[4]
310
        attempt = nagios_args[5]
311
        host = nagios_args[6]
312
    except IndexError:
313
        traceback.print_exc(limit=20)
314
        print('Number of Arguments given to the handler are incorrect')
315
        sys.exit(1)
316
317
    payload = {}
318
    payload['host'] = host
319
    payload['service'] = service
320
    payload['event_id'] = event_id
321
    payload['state'] = state
322
    payload['state_id'] = state_id
323
    payload['state_type'] = state_type
324
    payload['attempt'] = attempt
325
    payload['msg'] = STATE_MESSAGE.get(state, 'Undefined state.')
326
    return payload
327
328
329
def main(config_file, payload, verbose=False):
330
331
    _set_config_opts(config_file=config_file, verbose=verbose)
332
    _register_with_st2(verbose=verbose)
333
    _post_event_to_st2(payload, verbose=verbose)
334
335
336
if __name__ == '__main__':
337
    description = '\nStackStorm nagios event handler. Please provide args '\
338
        'in following order after the config_path:\n\n event_id, service, '\
339
        'state,state_id, state_type, attempt, host\n'
340
341
    parser = argparse.ArgumentParser(description)
342
    parser.add_argument('config_path',
343
                        help='Exchange to listen on')
344
    parser.add_argument('--verbose', '-v', required=False, action='store_true',
345
                        help='Verbose mode.')
346
347
    args, nagios_args = parser.parse_known_args()
348
    payload = _from_arg_to_payload(nagios_args)
349
350
    main(config_file=args.config_path, payload=payload, verbose=args.verbose)
351