Éric Lemoine | 6a1abe9 | 2016-11-03 14:02:43 +0000 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | import re |
| 4 | |
| 5 | _valid_dimension_re = re.compile(r'^[a-z0-9_/-]+$') |
| 6 | _disallowed_dimensions = ('name', 'value', 'hostname', 'member', |
Guillaume Thouvenin | 6faa862 | 2017-02-28 16:40:42 +0100 | [diff] [blame] | 7 | 'alerting_enabled', 'notification_enabled', |
| 8 | 'notification_handler', 'tag_fields') |
Éric Lemoine | 6a1abe9 | 2016-11-03 14:02:43 +0000 | [diff] [blame] | 9 | |
| 10 | |
Éric Lemoine | 7dc1213 | 2016-11-07 16:25:09 +0000 | [diff] [blame] | 11 | def alarm_message_matcher(alarm, triggers): |
Éric Lemoine | 6a1abe9 | 2016-11-03 14:02:43 +0000 | [diff] [blame] | 12 | """ |
| 13 | Return an Heka message matcher expression for a given alarm and a |
| 14 | dict of triggers. |
| 15 | |
| 16 | For example the function may return this: |
| 17 | |
| 18 | Fields[name] == 'cpu_idle' || Fields[name] = 'cpu_wait' |
| 19 | """ |
| 20 | matchers = set() |
| 21 | for trigger_name in alarm.get('triggers', []): |
| 22 | trigger = triggers.get(trigger_name) |
| 23 | if trigger and trigger.get('enabled', True): |
| 24 | for rule in trigger.get('rules', []): |
| 25 | matcher = "Fields[name] == '{}'".format(rule['metric']) |
| 26 | matchers.add(matcher) |
| 27 | return ' || '.join(matchers) |
| 28 | |
| 29 | |
Swann Croiset | eed005a | 2016-11-10 15:37:53 +0100 | [diff] [blame] | 30 | def alarm_activate_alerting(alerting): |
Éric Lemoine | f45901e | 2016-11-16 13:02:50 +0000 | [diff] [blame] | 31 | return ('true' if alerting in ['enabled', 'enabled_with_notification'] |
| 32 | else 'false') |
Swann Croiset | eed005a | 2016-11-10 15:37:53 +0100 | [diff] [blame] | 33 | |
| 34 | |
| 35 | def alarm_enable_notification(alerting): |
| 36 | return 'true' if alerting == 'enabled_with_notification' else 'false' |
| 37 | |
| 38 | |
Éric Lemoine | 7dc1213 | 2016-11-07 16:25:09 +0000 | [diff] [blame] | 39 | def alarm_cluster_message_matcher(alarm_cluster): |
| 40 | """ |
| 41 | Return an Heka message matcher expression for a given alarm cluster. |
| 42 | |
| 43 | For example the function may return this: |
| 44 | |
| 45 | Fields[service] == 'rabbitmq-cluster' |
| 46 | """ |
| 47 | matchers = set() |
| 48 | match_items = alarm_cluster.get('match', {}).items() |
| 49 | for match_name, match_value in match_items: |
| 50 | matcher = "Fields[{}] == '{}'".format(match_name, match_value) |
| 51 | matchers.add(matcher) |
| 52 | match_items = alarm_cluster.get('match_re', {}).items() |
| 53 | for match_name, match_value in match_items: |
| 54 | matcher = "Fields[{}] =~ /{}/".format(match_name, match_value) |
| 55 | matchers.add(matcher) |
| 56 | return ' && '.join(matchers) |
| 57 | |
| 58 | |
Éric Lemoine | fc2ae37 | 2016-11-08 13:55:03 +0000 | [diff] [blame] | 59 | def dimensions(alarm_or_alarm_cluster): |
Éric Lemoine | 6a1abe9 | 2016-11-03 14:02:43 +0000 | [diff] [blame] | 60 | """ |
| 61 | Return a dict alarm dimensions. Each dimension is validated, and an |
| 62 | Exception is raised if a dimension is invalid. |
| 63 | |
| 64 | Valid characters are a-z, 0-9, _, - and /. |
| 65 | """ |
Éric Lemoine | fc2ae37 | 2016-11-08 13:55:03 +0000 | [diff] [blame] | 66 | dimensions = alarm_or_alarm_cluster.get('dimension', {}) |
Éric Lemoine | 6a1abe9 | 2016-11-03 14:02:43 +0000 | [diff] [blame] | 67 | for name, value in dimensions.items(): |
| 68 | if name in _disallowed_dimensions: |
| 69 | raise Exception( |
| 70 | '{} is not allowed as a dimension name'.format(name)) |
| 71 | if not _valid_dimension_re.match(name): |
| 72 | raise Exception( |
| 73 | 'Dimension name {} includes disallowed chars'.format(name)) |
| 74 | if not _valid_dimension_re.match(value): |
| 75 | raise Exception( |
| 76 | 'Dimension value {} includes disallowed chars'.format(value)) |
| 77 | return dimensions |
Éric Lemoine | 74f7bd3 | 2016-11-15 13:18:33 +0000 | [diff] [blame] | 78 | |
| 79 | |
| 80 | def grains_for_mine(grains): |
| 81 | """ |
| 82 | Return grains that need to be sent to Salt Mine. Only the alarm |
| 83 | and alarm cluster data is to be sent to Mine. |
| 84 | """ |
| 85 | filtered_grains = {} |
| 86 | for service_name, service_data in grains.items(): |
Ilya Tyaptin | d31484e | 2016-12-12 19:25:07 +0400 | [diff] [blame] | 87 | if not service_data: |
| 88 | continue |
Éric Lemoine | 74f7bd3 | 2016-11-15 13:18:33 +0000 | [diff] [blame] | 89 | alarm = service_data.get('alarm') |
| 90 | if alarm: |
| 91 | filtered_grains[service_name] = {'alarm': alarm} |
Éric Lemoine | d26770f | 2016-11-16 13:01:59 +0000 | [diff] [blame] | 92 | trigger = service_data.get('trigger') |
| 93 | if trigger: |
| 94 | if service_name in filtered_grains: |
| 95 | filtered_grains[service_name].update( |
| 96 | {'trigger': trigger}) |
| 97 | else: |
| 98 | filtered_grains[service_name] = {'trigger': trigger} |
Éric Lemoine | 74f7bd3 | 2016-11-15 13:18:33 +0000 | [diff] [blame] | 99 | alarm_cluster = service_data.get('alarm_cluster') |
| 100 | if alarm_cluster: |
| 101 | if service_name in filtered_grains: |
| 102 | filtered_grains[service_name].update( |
| 103 | {'alarm_cluster': alarm_cluster}) |
| 104 | else: |
| 105 | filtered_grains[service_name] = \ |
| 106 | {'alarm_cluster': alarm_cluster} |
| 107 | return filtered_grains |