mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 01:18:58 +00:00
commit
88f8d337d9
@ -52,6 +52,7 @@ from salt.ext import six
|
||||
|
||||
try:
|
||||
from raven import Client
|
||||
from raven.transport.http import HTTPTransport
|
||||
|
||||
has_raven = True
|
||||
except ImportError:
|
||||
@ -77,88 +78,8 @@ def returner(ret):
|
||||
Failed states will be appended as separate list for convenience.
|
||||
'''
|
||||
|
||||
def ret_is_not_error(result):
|
||||
if result.get('return') and isinstance(result['return'], dict):
|
||||
result_dict = result['return']
|
||||
is_staterun = all('-' in key for key in result_dict.keys())
|
||||
if is_staterun:
|
||||
failed_states = {}
|
||||
for state_id, state_result in six.iteritems(result_dict):
|
||||
if not state_result['result']:
|
||||
failed_states[state_id] = state_result
|
||||
|
||||
if failed_states:
|
||||
result['failed_states'] = failed_states
|
||||
return False
|
||||
|
||||
if result.get('success') and result.get('retcode', 0) == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_message():
|
||||
return 'func: {fun}, jid: {jid}'.format(fun=ret['fun'], jid=ret['jid'])
|
||||
|
||||
def connect_sentry(message, result):
|
||||
'''
|
||||
Connect to the Sentry server
|
||||
'''
|
||||
pillar_data = __salt__['pillar.raw']()
|
||||
grains = __salt__['grains.items']()
|
||||
raven_config = pillar_data['raven']
|
||||
hide_pillar = raven_config.get('hide_pillar')
|
||||
sentry_data = {
|
||||
'result': result,
|
||||
'pillar': 'HIDDEN' if hide_pillar else pillar_data,
|
||||
'grains': grains
|
||||
}
|
||||
data = {
|
||||
'platform': 'python',
|
||||
'culprit': message,
|
||||
'level': 'error'
|
||||
}
|
||||
tags = {}
|
||||
if 'tags' in raven_config:
|
||||
for tag in raven_config['tags']:
|
||||
tags[tag] = grains[tag]
|
||||
|
||||
if ret_is_not_error(ret):
|
||||
data['level'] = 'info'
|
||||
|
||||
if raven_config.get('report_errors_only') and data['level'] != 'error':
|
||||
return
|
||||
|
||||
if raven_config.get('dsn'):
|
||||
client = Client(raven_config.get('dsn'))
|
||||
else:
|
||||
try:
|
||||
servers = []
|
||||
for server in raven_config['servers']:
|
||||
servers.append(server + '/api/store/')
|
||||
client = Client(
|
||||
servers=servers,
|
||||
public_key=raven_config['public_key'],
|
||||
secret_key=raven_config['secret_key'],
|
||||
project=raven_config['project']
|
||||
)
|
||||
except KeyError as missing_key:
|
||||
logger.error(
|
||||
'Sentry returner needs key \'%s\' in pillar',
|
||||
missing_key
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
msgid = client.capture('raven.events.Message', message=message, data=data, extra=sentry_data, tags=tags)
|
||||
logger.info('Message id %s written to sentry', msgid)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
'Can\'t send message to sentry: {0}'.format(exc),
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
try:
|
||||
connect_sentry(get_message(), ret)
|
||||
_connect_sentry(_get_message(ret), ret)
|
||||
except Exception as err:
|
||||
logger.error(
|
||||
'Can\'t run connect_sentry: {0}'.format(err),
|
||||
@ -166,6 +87,105 @@ def returner(ret):
|
||||
)
|
||||
|
||||
|
||||
def _ret_is_not_error(result):
|
||||
if result.get('return') and isinstance(result['return'], dict):
|
||||
result_dict = result['return']
|
||||
is_staterun = all('-' in key for key in result_dict.keys())
|
||||
if is_staterun:
|
||||
failed_states = {}
|
||||
for state_id, state_result in six.iteritems(result_dict):
|
||||
if not state_result['result']:
|
||||
failed_states[state_id] = state_result
|
||||
|
||||
if failed_states:
|
||||
result['failed_states'] = failed_states
|
||||
return False
|
||||
return True
|
||||
|
||||
if result.get('success'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _get_message(ret):
|
||||
if not ret.get('fun_args'):
|
||||
return 'salt func: {}'.format(ret['fun'])
|
||||
arg_string = ' '.join([arg for arg in ret['fun_args'] if isinstance(arg, six.string_types)])
|
||||
kwarg_string = ''
|
||||
if isinstance(ret['fun_args'], list) and len(ret['fun_args']) > 0:
|
||||
kwargs = ret['fun_args'][-1]
|
||||
if isinstance(kwargs, dict):
|
||||
kwarg_string = ' '.join(sorted(['{}={}'.format(k, v) for k, v in kwargs.items() if not k.startswith('_')]))
|
||||
return 'salt func: {fun} {argstr} {kwargstr}'.format(fun=ret['fun'], argstr=arg_string, kwargstr=kwarg_string).strip()
|
||||
|
||||
|
||||
def _connect_sentry(message, result):
|
||||
'''
|
||||
Connect to the Sentry server
|
||||
'''
|
||||
pillar_data = __salt__['pillar.raw']()
|
||||
grains = __salt__['grains.items']()
|
||||
raven_config = pillar_data['raven']
|
||||
hide_pillar = raven_config.get('hide_pillar')
|
||||
sentry_data = {
|
||||
'result': result,
|
||||
'pillar': 'HIDDEN' if hide_pillar else pillar_data,
|
||||
'grains': grains
|
||||
}
|
||||
data = {
|
||||
'platform': 'python',
|
||||
'culprit': message,
|
||||
'level': 'error'
|
||||
}
|
||||
tags = {}
|
||||
if 'tags' in raven_config:
|
||||
for tag in raven_config['tags']:
|
||||
tags[tag] = grains[tag]
|
||||
|
||||
if _ret_is_not_error(result):
|
||||
data['level'] = 'info'
|
||||
|
||||
if raven_config.get('report_errors_only') and data['level'] != 'error':
|
||||
return
|
||||
|
||||
if raven_config.get('dsn'):
|
||||
client = Client(raven_config.get('dsn'), transport=HTTPTransport)
|
||||
else:
|
||||
try:
|
||||
servers = []
|
||||
for server in raven_config['servers']:
|
||||
servers.append(server + '/api/store/')
|
||||
client = Client(
|
||||
servers=servers,
|
||||
public_key=raven_config['public_key'],
|
||||
secret_key=raven_config['secret_key'],
|
||||
project=raven_config['project'],
|
||||
transport=HTTPTransport
|
||||
)
|
||||
except KeyError as missing_key:
|
||||
logger.error(
|
||||
'Sentry returner needs key \'%s\' in pillar',
|
||||
missing_key
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
msgid = client.capture(
|
||||
'raven.events.Message',
|
||||
message=message,
|
||||
data=data,
|
||||
extra=sentry_data,
|
||||
tags=tags
|
||||
)
|
||||
logger.info('Message id {} written to sentry'.format(msgid))
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
'Can\'t send message to sentry: {0}'.format(exc),
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
|
||||
def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument
|
||||
'''
|
||||
Do any work necessary to prepare a JID, including sending a custom id
|
||||
|
26
tests/unit/returners/test_sentry.py
Normal file
26
tests/unit/returners/test_sentry.py
Normal file
@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.unit import TestCase
|
||||
|
||||
# Import salt libs
|
||||
import salt.returners.sentry_return as sentry
|
||||
|
||||
|
||||
class SentryReturnerTestCase(TestCase):
|
||||
'''
|
||||
Test Sentry Returner
|
||||
'''
|
||||
ret = {'id': '12345',
|
||||
'fun': 'mytest.func',
|
||||
'fun_args': ['arg1', 'arg2', {'foo': 'bar'}],
|
||||
'jid': '54321',
|
||||
'return': 'Long Return containing a Traceback'}
|
||||
|
||||
def test_get_message(self):
|
||||
self.assertEqual(sentry._get_message(self.ret), 'salt func: mytest.func arg1 arg2 foo=bar')
|
||||
self.assertEqual(sentry._get_message({'fun': 'test.func', 'fun_args': []}), 'salt func: test.func')
|
||||
self.assertEqual(sentry._get_message({'fun': 'test.func'}), 'salt func: test.func')
|
Loading…
Reference in New Issue
Block a user