Skip to content

Commit

Permalink
aws.health-event filter for additional resources (cloud-custodian#2943)
Browse files Browse the repository at this point in the history
  • Loading branch information
taohungyang authored and kapilt committed Oct 4, 2018
1 parent 9d4ec75 commit 0beaa59
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 17 deletions.
1 change: 1 addition & 0 deletions c7n/filters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
AgeFilter,
EventFilter)
from .config import ConfigCompliance
from .health import HealthEventFilter
from .iamaccess import CrossAccountAccessFilter, PolicyChecker
from .metrics import MetricsFilter, ShieldMetrics
from .vpc import DefaultVpcBase
43 changes: 33 additions & 10 deletions c7n/filters/health.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from c7n.utils import local_session, chunks, type_schema
from .core import Filter
from c7n.manager import resources


class HealthEventFilter(Filter):
Expand All @@ -43,15 +44,19 @@ def process(self, resources, event=None):
client = local_session(self.manager.session_factory).client(
'health', region_name='us-east-1')
f = self.get_filter_parameters()
resource_map = {r[self.manager.get_model().id]: r for r in resources}
if self.manager.data['resource'] in {'app-elb'}:
id_attr = self.manager.get_model().name
else:
id_attr = self.manager.get_model().id
resource_map = {r[id_attr]: r for r in resources}
found = set()
seen = set()

for resource_set in chunks(resource_map.keys(), 100):
f['entityValues'] = resource_set
events = client.describe_events(filter=f)['events']
events = [e for e in events if e['arn'] not in seen]
entities = self.process_event(events)
entities = self.process_event(client, events)

event_map = {e['arn']: e for e in events}
for e in entities:
Expand All @@ -65,23 +70,25 @@ def process(self, resources, event=None):
return [resource_map[resource_id] for resource_id in found]

def get_filter_parameters(self):
phd_svc_name_map = {
'app-elb': 'ELASTICLOADBALANCING',
'ebs': 'EBS',
'efs': 'ELASTICFILESYSTEM',
'elb': 'ELASTICLOADBALANCING',
'emr': 'ELASTICMAPREDUCE'
}
m = self.manager
if m.data['resource'] == 'ebs':
service = 'EBS'
else:
service = m.get_model().service.upper()
service = phd_svc_name_map.get(m.data['resource'], m.get_model().service.upper())
f = {'services': [service],
'regions': [self.manager.config.region],
'regions': [self.manager.config.region, 'global'],
'eventStatusCodes': self.data.get(
'statuses', ['open', 'upcoming'])}
if self.data.get('types'):
f['eventTypeCodes'] = self.data.get('types')
return f

def process_event(self, health_events):
def process_event(self, client, health_events):
entities = []
client = local_session(self.manager.session_factory).client(
'health', region_name='us-east-1')
for event_set in chunks(health_events, 10):
event_map = {e['arn']: e for e in event_set}
event_arns = list(event_map.keys())
Expand All @@ -94,3 +101,19 @@ def process_event(self, health_events):
*[p['entities'] for p in paginator.paginate(
filter={'eventArns': event_arns})])))
return entities

@classmethod
def register_resources(klass, registry, resource_class):
""" meta model subscriber on resource registration.
We watch for PHD event that provides affected entities and register
the health-event filter to the resources.
"""
services = {'acm-certificate', 'directconnect', 'dms-instance', 'directory', 'ec2',
'dynamodb-table', 'cache-cluster', 'efs', 'app-elb', 'elb', 'emr', 'rds',
'storage-gateway'}
if resource_class.type in services:
resource_class.filter_registry.register('health-event', klass)


resources.subscribe(resources.EVENT_REGISTER, HealthEventFilter.register_resources)
2 changes: 1 addition & 1 deletion c7n/resources/ebs.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ def process(self, resources, event=None):
paginator = client.get_paginator('describe_events')
events = list(itertools.chain(
*[p['events']for p in paginator.paginate(filter=f)]))
entities = self.process_event(events)
entities = self.process_event(client, events)

event_map = {e['arn']: e for e in events}
config = local_session(self.manager.session_factory).client('config')
Expand Down
3 changes: 0 additions & 3 deletions c7n/resources/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
FilterRegistry, AgeFilter, ValueFilter, Filter, OPERATORS, DefaultVpcBase
)
from c7n.filters.offhours import OffHour, OnHour
from c7n.filters.health import HealthEventFilter
import c7n.filters.vpc as net_filters

from c7n.manager import resources
Expand All @@ -46,8 +45,6 @@
filters = FilterRegistry('ec2.filters')
actions = ActionRegistry('ec2.actions')

filters.register('health-event', HealthEventFilter)


@resources.register('ec2')
class EC2(query.QueryResourceManager):
Expand Down
3 changes: 0 additions & 3 deletions c7n/resources/rds.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
OPERATORS)

from c7n.filters.offhours import OffHour, OnHour
from c7n.filters.health import HealthEventFilter
import c7n.filters.vpc as net_filters
from c7n.manager import resources
from c7n.query import QueryResourceManager, DescribeSource, ConfigSource
Expand All @@ -80,8 +79,6 @@
filters = FilterRegistry('rds.filters')
actions = ActionRegistry('rds.actions')

filters.register('health-event', HealthEventFilter)


@resources.register('rds')
class RDS(QueryResourceManager):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"status_code": 200,
"data": {
"LoadBalancers": [
{
"VpcId": "vpc-4a9ff72e",
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-alb/ff1789434bfa3896",
"State": {
"Code": "active"
},
"DNSName": "test-alb.us-east-1.elb.amazonaws.com",
"SecurityGroups": [
"sg-f9cc4d9f"
],
"LoadBalancerName": "test-alb",
"CreatedTime": {
"hour": 17,
"__class__": "datetime",
"month": 1,
"second": 20,
"microsecond": 10000,
"year": 2018,
"day": 26,
"minute": 52
},
"Scheme": "internal",
"Type": "application",
"CanonicalHostedZoneId": "Z1H1FL5HABSF5",
"AvailabilityZones": [
{
"SubnetId": "subnet-15452171",
"ZoneName": "us-east-1a"
},
{
"SubnetId": "subnet-5fb47507",
"ZoneName": "us-east-1c"
}
]
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "de4574a5-8653-11e6-b76c-c5e64157571b",
"HTTPHeaders": {
"x-amzn-requestid": "de4574a5-8653-11e6-b76c-c5e64157571b",
"vary": "Accept-Encoding",
"content-length": "2484",
"content-type": "text/xml",
"date": "Mon, 01 Oct 2018 20:32:13 GMT"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"status_code": 200,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "fae221d1-8653-11e6-9612-e99914e6ef67",
"HTTPHeaders": {
"x-amzn-requestid": "fae221d1-8653-11e6-9612-e99914e6ef67",
"date": "Mon, 01 Oct 2018 20:32:19 GMT",
"content-length": "877",
"content-type": "text/xml"
}
},
"TagDescriptions": [
{
"ResourceArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-alb/ff1789434bfa3896",
"Tags": [
{
"Value": "VALUE2",
"Key": "KEY2"
},
{
"Value": "VALUE1",
"Key": "KEY1"
}
]
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"status_code": 200,
"data": {
"entities": [
{
"entityValue": "test-alb",
"lastUpdatedTime": {
"hour": 19,
"__class__": "datetime",
"month": 7,
"second": 9,
"microsecond": 183000,
"year": 2018,
"day": 23,
"minute": 47
},
"entityArn": "arn:aws:health:us-east-1:123456789012:entity/QWERtyuiOPLKjhgfDCvb",
"awsAccountId": "123456789012",
"eventArn": "arn:aws:health:us-east-1::event/ELASTICLOADBALANCING/AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET/AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET_1234567890_12345678901",
"statusCode": "UNIMPAIRED"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "1d866abf-c5b9-11e8-af28-6d15edc46571",
"HTTPHeaders": {
"x-amzn-requestid": "1d866abf-c5b9-11e8-af28-6d15edc46571",
"date": "Mon, 01 Oct 2018 20:32:28 GMT",
"content-length": "450",
"content-type": "application/x-amz-json-1.1"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"status_code": 200,
"data": {
"failedSet": [],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "1d2d4e33-c5b9-11e8-af28-6d15edc46571",
"HTTPHeaders": {
"x-amzn-requestid": "1d2d4e33-c5b9-11e8-af28-6d15edc46571",
"date": "Mon, 01 Oct 2018 20:32:28 GMT",
"content-length": "1515",
"content-type": "application/x-amz-json-1.1"
}
},
"successfulSet": [
{
"event": {
"lastUpdatedTime": {
"hour": 19,
"__class__": "datetime",
"month": 7,
"second": 9,
"microsecond": 784000,
"year": 2018,
"day": 23,
"minute": 47
},
"service": "ELASTICLOADBALANCING",
"eventTypeCode": "AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET",
"startTime": {
"hour": 18,
"__class__": "datetime",
"month": 7,
"second": 26,
"microsecond": 0,
"year": 2018,
"day": 23,
"minute": 32
},
"eventTypeCategory": "issue",
"endTime": {
"hour": 19,
"__class__": "datetime",
"month": 7,
"second": 9,
"microsecond": 0,
"year": 2018,
"day": 23,
"minute": 47
},
"region": "us-east-1",
"arn": "arn:aws:health:us-east-1::event/ELASTICLOADBALANCING/AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET/AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET_1234567890_12345678901",
"statusCode": "closed"
},
"eventDescription": {
"latestDescription": "We have detected an issue with the ELBs associated with this event in the us-east-1 region.\n\nThe load balancers are unable to scale up because there are not enough free IP addresses in its subnets. The best practice is to keep at least eight IP addresses available in each subnet.\n\nThere are two ways to address the issue:\n- Configure the load balancers to use a different subnet in the same Availability Zone that has more free IP addresses.\n- Free up IP addresses in the current subnets by deleting unused ENIs or moving other resources to different subnets.\n\nWe strongly recommend that you address this issue to ensure that your load balancers are prepared to handle increases in traffic. For more information, see http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-manage-subnets.html.\n\nIf you require assistance to resolve this issue, contact us by opening a new support case at https://console.aws.amazon.com/support/home."
}
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"status_code": 200,
"data": {
"events": [
{
"lastUpdatedTime": {
"hour": 19,
"__class__": "datetime",
"month": 7,
"second": 9,
"microsecond": 784000,
"year": 2018,
"day": 23,
"minute": 47
},
"service": "ELASTICLOADBALANCING",
"eventTypeCode": "AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET",
"startTime": {
"hour": 18,
"__class__": "datetime",
"month": 7,
"second": 26,
"microsecond": 0,
"year": 2018,
"day": 23,
"minute": 32
},
"eventTypeCategory": "issue",
"endTime": {
"hour": 19,
"__class__": "datetime",
"month": 7,
"second": 9,
"microsecond": 0,
"year": 2018,
"day": 23,
"minute": 47
},
"region": "us-east-1",
"arn": "arn:aws:health:us-east-1::event/ELASTICLOADBALANCING/AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET/AWS_ELASTICLOADBALANCING_INSUFFICIENT_IPS_IN_SUBNET_1234567890_12345678901",
"statusCode": "closed"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "1c9a5cef-c5b9-11e8-af28-6d15edc46571",
"HTTPHeaders": {
"x-amzn-requestid": "1c9a5cef-c5b9-11e8-af28-6d15edc46571",
"date": "Mon, 01 Oct 2018 20:32:27 GMT",
"content-length": "478",
"content-type": "application/x-amz-json-1.1"
}
}
}
}
Loading

0 comments on commit 0beaa59

Please sign in to comment.