| # -*- coding: utf-8 -*- |
| |
| import re |
| |
| _valid_dimension_re = re.compile(r'^[a-z0-9_/-]+$') |
| _disallowed_dimensions = ('name', 'value', 'hostname', 'member', |
| 'no_alerting', 'tag_fields') |
| |
| |
| def alarm_message_matcher(alarm, triggers): |
| """ |
| Return an Heka message matcher expression for a given alarm and a |
| dict of triggers. |
| |
| For example the function may return this: |
| |
| Fields[name] == 'cpu_idle' || Fields[name] = 'cpu_wait' |
| """ |
| matchers = set() |
| for trigger_name in alarm.get('triggers', []): |
| trigger = triggers.get(trigger_name) |
| if trigger and trigger.get('enabled', True): |
| for rule in trigger.get('rules', []): |
| matcher = "Fields[name] == '{}'".format(rule['metric']) |
| matchers.add(matcher) |
| return ' || '.join(matchers) |
| |
| |
| def alarm_activate_alerting(alerting): |
| return ('true' if alerting in ['enabled', 'enabled_with_notification'] |
| else 'false') |
| |
| |
| def alarm_enable_notification(alerting): |
| return 'true' if alerting == 'enabled_with_notification' else 'false' |
| |
| |
| def alarm_cluster_message_matcher(alarm_cluster): |
| """ |
| Return an Heka message matcher expression for a given alarm cluster. |
| |
| For example the function may return this: |
| |
| Fields[service] == 'rabbitmq-cluster' |
| """ |
| matchers = set() |
| match_items = alarm_cluster.get('match', {}).items() |
| for match_name, match_value in match_items: |
| matcher = "Fields[{}] == '{}'".format(match_name, match_value) |
| matchers.add(matcher) |
| match_items = alarm_cluster.get('match_re', {}).items() |
| for match_name, match_value in match_items: |
| matcher = "Fields[{}] =~ /{}/".format(match_name, match_value) |
| matchers.add(matcher) |
| return ' && '.join(matchers) |
| |
| |
| def dimensions(alarm_or_alarm_cluster): |
| """ |
| Return a dict alarm dimensions. Each dimension is validated, and an |
| Exception is raised if a dimension is invalid. |
| |
| Valid characters are a-z, 0-9, _, - and /. |
| """ |
| dimensions = alarm_or_alarm_cluster.get('dimension', {}) |
| for name, value in dimensions.items(): |
| if name in _disallowed_dimensions: |
| raise Exception( |
| '{} is not allowed as a dimension name'.format(name)) |
| if not _valid_dimension_re.match(name): |
| raise Exception( |
| 'Dimension name {} includes disallowed chars'.format(name)) |
| if not _valid_dimension_re.match(value): |
| raise Exception( |
| 'Dimension value {} includes disallowed chars'.format(value)) |
| return dimensions |
| |
| |
| def grains_for_mine(grains): |
| """ |
| Return grains that need to be sent to Salt Mine. Only the alarm |
| and alarm cluster data is to be sent to Mine. |
| """ |
| filtered_grains = {} |
| for service_name, service_data in grains.items(): |
| alarm = service_data.get('alarm') |
| if alarm: |
| filtered_grains[service_name] = {'alarm': alarm} |
| trigger = service_data.get('trigger') |
| if trigger: |
| if service_name in filtered_grains: |
| filtered_grains[service_name].update( |
| {'trigger': trigger}) |
| else: |
| filtered_grains[service_name] = {'trigger': trigger} |
| alarm_cluster = service_data.get('alarm_cluster') |
| if alarm_cluster: |
| if service_name in filtered_grains: |
| filtered_grains[service_name].update( |
| {'alarm_cluster': alarm_cluster}) |
| else: |
| filtered_grains[service_name] = \ |
| {'alarm_cluster': alarm_cluster} |
| return filtered_grains |