Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws - add cwa filter for alarms that have a parent composite alarm #9300

Merged
27 changes: 26 additions & 1 deletion c7n/resources/cw.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
from c7n.resources import load_resources
from c7n.resources.aws import ArnResolver
from c7n.tags import universal_augment
from c7n.utils import type_schema, local_session, chunks, get_retry
from c7n.utils import type_schema, local_session, chunks, get_retry, jmespath_search
from botocore.config import Config
import re


class DescribeAlarm(DescribeSource):
Expand Down Expand Up @@ -89,6 +90,30 @@ def process(self, resources):
AlarmNames=[r['AlarmName'] for r in resource_set])


@Alarm.filter_registry.register('exclude-composite-child')
class ExcludeCompositeChild(Filter):
schema = type_schema('exclude-composite-alarms')
timmygrable marked this conversation as resolved.
Show resolved Hide resolved
permissions = ('cloudwatch:DescribeAlarms',)

def process(self, resources, event=None):
# Get the composite alarms since filtered out in enum_spec
composite_alarms = self.manager.get_resource_manager("composite-alarm").resources()
composite_alarm_rules = jmespath_search('[].AlarmRule', composite_alarms)

parent_alarm_names = set()
# Loop through, find child alarm names
for rule in composite_alarm_rules:
names = self.extract_alarm_names_from_rule(rule)
parent_alarm_names.update(names)

# Return alarms that aren't a child alarm
return [r for r in resources if r['AlarmName'] not in parent_alarm_names]

def extract_alarm_names_from_rule(self, rule):
pattern = r"ALARM\(([^)]+)\)"
timmygrable marked this conversation as resolved.
Show resolved Hide resolved
matches = re.findall(pattern, rule)
return set(matches)

@resources.register('composite-alarm')
class CompositeAlarm(QueryResourceManager):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"status_code": 200,
"data": {
"CompositeAlarms": [],
"MetricAlarms": [
{
"AlarmName": "c7n-test-alarm-tags-filter",
"AlarmArn": "arn:aws:cloudwatch:us-east-1:644160558196:alarm:c7n-test-alarm-tags-filter",
"AlarmConfigurationUpdatedTimestamp": {
"__class__": "datetime",
"year": 2021,
"month": 3,
"day": 24,
"hour": 18,
"minute": 1,
"second": 32,
"microsecond": 583000
},
"ActionsEnabled": true,
"OKActions": [],
"AlarmActions": [],
"InsufficientDataActions": [],
"StateValue": "INSUFFICIENT_DATA",
"StateReason": "Insufficient Data: 5 datapoints were unknown.",
"StateReasonData": "{\"version\":\"1.0\",\"queryDate\":\"2022-09-27T10:05:12.193+0000\",\"statistic\":\"Average\",\"period\":3600,\"recentDatapoints\":[],\"threshold\":10.0,\"evaluatedDatapoints\":[{\"timestamp\":\"2022-09-27T09:05:00.000+0000\"},{\"timestamp\":\"2022-09-27T08:05:00.000+0000\"},{\"timestamp\":\"2022-09-27T07:05:00.000+0000\"},{\"timestamp\":\"2022-09-27T06:05:00.000+0000\"},{\"timestamp\":\"2022-09-27T05:05:00.000+0000\"}]}",
"StateUpdatedTimestamp": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 27,
"hour": 10,
"minute": 5,
"second": 12,
"microsecond": 198000
},
"MetricName": "CPUUtilization",
"Namespace": "AWS/EC2",
"Statistic": "Average",
"Dimensions": [],
"Period": 3600,
"EvaluationPeriods": 5,
"Threshold": 10.0,
"ComparisonOperator": "GreaterThanThreshold",
"StateTransitionedTimestamp": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 27,
"hour": 10,
"minute": 5,
"second": 12,
"microsecond": 198000
}
}
],
"ResponseMetadata": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"status_code": 200,
"data": {
"CompositeAlarms": [
{
"ActionsEnabled": true,
"AlarmActions": [
"arn:aws:sns:us-east-1:644160558196:test-xyz"
],
"AlarmArn": "arn:aws:cloudwatch:us-east-1:644160558196:alarm:ExampleCompositeAlarm",
"AlarmConfigurationUpdatedTimestamp": {
"__class__": "datetime",
"year": 2024,
"month": 2,
"day": 16,
"hour": 16,
"minute": 32,
"second": 54,
"microsecond": 256000
},
"AlarmDescription": "Composite alarm based on c7n-test-alarm-tags-filter",
"AlarmName": "ExampleCompositeAlarm",
"AlarmRule": "ALARM(c7n-test-alarm-tags-filter)",
"InsufficientDataActions": [
"arn:aws:sns:us-east-1:644160558196:test-zyx"
],
"OKActions": [
"arn:aws:sns:us-east-1:644160558196:test-xyz"
],
"StateReason": "arn:aws:cloudwatch:us-east-1:644160558196:alarm:ExampleCompositeAlarm was created and its alarm rule evaluates to OK",
"StateReasonData": "{\"triggeringAlarms\":[{\"arn\":\"arn:aws:cloudwatch:us-east-1:644160558196:alarm:c7n-test-alarm-tags-filter\",\"state\":{\"value\":\"INSUFFICIENT_DATA\",\"timestamp\":\"2022-09-27T10:05:12.198+0000\"}}]}",
"StateUpdatedTimestamp": {
"__class__": "datetime",
"year": 2024,
"month": 2,
"day": 16,
"hour": 16,
"minute": 32,
"second": 54,
"microsecond": 256000
},
"StateValue": "OK",
"StateTransitionedTimestamp": {
"__class__": "datetime",
"year": 2024,
"month": 2,
"day": 16,
"hour": 16,
"minute": 32,
"second": 54,
"microsecond": 256000
}
}
],
"MetricAlarms": [],
"ResponseMetadata": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"status_code": 200,
"data": {
"PaginationToken": "",
"ResourceTagMappingList": [
{
"ResourceARN": "arn:aws:cloudwatch:us-east-1:644160558196:alarm:c7n-test-alarm-tags-filter",
"Tags": [
{
"Key": "some-tag",
"Value": "some-value"
},
{
"Key": "OwnerName",
"Value": "SomeName"
},
{
"Key": "pratyush",
"Value": "test"
}
]
}
],
"ResponseMetadata": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"status_code": 200,
"data": {
"PaginationToken": "",
"ResourceTagMappingList": [
{
"ResourceARN": "arn:aws:cloudwatch:us-east-1:644160558196:alarm:ExampleCompositeAlarm",
"Tags": []
}
],
"ResponseMetadata": {}
}
}
21 changes: 21 additions & 0 deletions tests/test_cwa.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ def test_add_alarm_tags(self):
self.assertEqual(len(resources), 1)
self.assertTrue({'Key': 'OwnerName', 'Value': 'SomeName'} in resources[0].get('Tags'))

def test_exclude_composite_child_filter(self):
factory = self.replay_flight_data("test_exclude_composite_child_filter")
p = self.load_policy(
{
"name": "exclude-composite-child",
"resource": "aws.alarm",
"filters": [
{
'type': 'exclude-composite-child',
}
],
},
session_factory=factory,
)

resources = p.run()
self.assertEqual(len(resources), 0)

for alarm in resources:
self.assertNotIn(alarm['AlarmName'], "c7n-test-alarm-tags-filter")


class CompositeAlarmTest(BaseTest):

Expand Down