diff --git a/awslimitchecker/services/vpc.py b/awslimitchecker/services/vpc.py index 0080fe5b..7153aad0 100644 --- a/awslimitchecker/services/vpc.py +++ b/awslimitchecker/services/vpc.py @@ -71,6 +71,7 @@ def find_usage(self): self._find_usage_route_tables() self._find_usage_gateways() self._find_usage_nat_gateways(subnet_to_az) + self._find_usage_peering_connections() self._find_usages_vpn_gateways() self._find_usage_network_interfaces() self._have_usage = True @@ -216,6 +217,33 @@ def _find_usage_nat_gateways(self, subnet_to_az): 'perhaps NAT service does not exist in this region?', exc_info=1) + def _find_usage_peering_connections(self): + """find usage of Active VPC Peering Connections""" + + peering_connections = self.conn.describe_vpc_peering_connections( + Filters=[{'Name': 'status-code', 'Values': ['active']}] + )['VpcPeeringConnections'] + + peering_connections_per_vpc = defaultdict(int) + for peering in peering_connections: + for vpc in [peering['AccepterVpcInfo'], peering['RequesterVpcInfo']]: + # Each peering contains 2 VPCs and at least one is in our current account and region. To avoid incorrect alerts about out-of-scope VPCs we need to filter to only VPCs in our account and region. + + if vpc['OwnerId'] != self.current_account_id: + continue + + if vpc['Region'] != "us-west-2": # TODO: How to get current region? + continue + + peering_connections_per_vpc[vpc['VpcId']] += 1 + + for vpc_id, peerings in peering_connections_per_vpc.items(): + self.limits['Active VPC peering connections per VPC']._add_current_usage( + peerings, + aws_type='AWS::EC2::VPC', + resource_id=vpc_id + ) + def _find_usages_vpn_gateways(self): """find usage of vpn gateways""" @@ -343,6 +371,16 @@ def get_limits(self): quotas_name='NAT gateways per Availability Zone' ) + limits['Active VPC peering connections per VPC'] = AwsLimit( + 'Active VPC peering connections per VPC', + self, + 50, + self.warning_threshold, + self.critical_threshold, + limit_type='AWS::EC2::VPCPeeringConnection', + limit_subtype='AWS::EC2::VPC', + ) + limits['Virtual private gateways'] = AwsLimit( 'Virtual private gateways', self, diff --git a/awslimitchecker/tests/services/result_fixtures.py b/awslimitchecker/tests/services/result_fixtures.py index 93c68929..c93f0413 100644 --- a/awslimitchecker/tests/services/result_fixtures.py +++ b/awslimitchecker/tests/services/result_fixtures.py @@ -617,6 +617,122 @@ class VPC(object): 'subnet3': 'az2' } + test_find_usage_peering_connections = { + 'VpcPeeringConnections': [ + { + 'AccepterVpcInfo': { + 'CidrBlock': '10.3.128.0/18', + 'CidrBlockSet': [{'CidrBlock': '10.3.128.0/18'}], + 'OwnerId': '123456789012', + 'PeeringOptions': { + 'AllowDnsResolutionFromRemoteVpc': False, + 'AllowEgressFromLocalClassicLinkToRemoteVpc': False, + 'AllowEgressFromLocalVpcToRemoteClassicLink': False, + }, + 'Region': 'us-west-2', + 'VpcId': 'vpc-1', + }, + 'RequesterVpcInfo': { + 'CidrBlock': '10.8.72.0/21', + 'CidrBlockSet': [{'CidrBlock': '10.8.72.0/21'}], + 'OwnerId': '123456789012', + 'PeeringOptions': { + 'AllowDnsResolutionFromRemoteVpc': False, + 'AllowEgressFromLocalClassicLinkToRemoteVpc': False, + 'AllowEgressFromLocalVpcToRemoteClassicLink': False, + }, + 'Region': 'ap-south-1', + 'VpcId': 'vpc-2', + }, + 'Status': { + 'Code': 'active', + 'Message': 'Active', + }, + 'Tags': [ + { + 'Key': 'a key', + 'Value': 'a value', + }, + ], + 'VpcPeeringConnectionId': 'pcx-1', + }, + { + 'AccepterVpcInfo': { + 'CidrBlock': '10.3.128.0/18', + 'CidrBlockSet': [{'CidrBlock': '10.3.128.0/18'}], + 'OwnerId': '99999', + 'PeeringOptions': { + 'AllowDnsResolutionFromRemoteVpc': False, + 'AllowEgressFromLocalClassicLinkToRemoteVpc': False, + 'AllowEgressFromLocalVpcToRemoteClassicLink': False, + }, + 'Region': 'us-west-2', + 'VpcId': 'vpc-3', + }, + 'RequesterVpcInfo': { + 'CidrBlock': '10.8.72.0/21', + 'CidrBlockSet': [{'CidrBlock': '10.8.72.0/21'}], + 'OwnerId': '123456789012', + 'PeeringOptions': { + 'AllowDnsResolutionFromRemoteVpc': False, + 'AllowEgressFromLocalClassicLinkToRemoteVpc': False, + 'AllowEgressFromLocalVpcToRemoteClassicLink': False, + }, + 'Region': 'us-west-2', + 'VpcId': 'vpc-1', + }, + 'Status': { + 'Code': 'active', + 'Message': 'Active', + }, + 'Tags': [ + { + 'Key': 'foo', + 'Value': 'bar', + }, + ], + 'VpcPeeringConnectionId': 'pcx-2', + }, + { + 'AccepterVpcInfo': { + 'CidrBlock': '10.3.128.0/18', + 'CidrBlockSet': [{'CidrBlock': '10.3.128.0/18'}], + 'OwnerId': '123456789012', + 'PeeringOptions': { + 'AllowDnsResolutionFromRemoteVpc': False, + 'AllowEgressFromLocalClassicLinkToRemoteVpc': False, + 'AllowEgressFromLocalVpcToRemoteClassicLink': False, + }, + 'Region': 'us-west-2', + 'VpcId': 'vpc-4', + }, + 'RequesterVpcInfo': { + 'CidrBlock': '10.8.72.0/21', + 'CidrBlockSet': [{'CidrBlock': '10.8.72.0/21'}], + 'OwnerId': '123456789012', + 'PeeringOptions': { + 'AllowDnsResolutionFromRemoteVpc': False, + 'AllowEgressFromLocalClassicLinkToRemoteVpc': False, + 'AllowEgressFromLocalVpcToRemoteClassicLink': False, + }, + 'Region': 'ap-south-1', + 'VpcId': 'vpc-5', + }, + 'Status': { + 'Code': 'active', + 'Message': 'Active', + }, + 'Tags': [ + { + 'Key': 'out', + 'Value': 'of ideas', + }, + ], + 'VpcPeeringConnectionId': 'pcx-3', + }, + ] + } + test_find_usages_vpn_gateways = { 'VpnGateways': [ { diff --git a/awslimitchecker/tests/services/test_vpc.py b/awslimitchecker/tests/services/test_vpc.py index 60003ace..49ae64af 100644 --- a/awslimitchecker/tests/services/test_vpc.py +++ b/awslimitchecker/tests/services/test_vpc.py @@ -334,6 +334,30 @@ def se_exc(*args, **kwargs): exc_info=1) ] + def test_find_usage_peering_connections(self): + response = result_fixtures.VPC.test_find_usage_peering_connections + + mock_conn = Mock() + mock_conn.describe_vpc_peering_connections.return_value = response + cls = _VpcService(21, 43, {}, None) + cls._current_account_id = '0123456789' + cls.conn = mock_conn + + res = cls._find_usage_peering_connections() + + usage = sorted(cls.limits['Active VPC peering connections per VPC'].get_current_usage()) + print(usage) + assert len(usage) == 2 + assert usage[0].resource_id == 'vpc-4' + assert usage[0].get_value() == 1 + assert usage[1].resource_id == 'vpc-3' + assert usage[1].get_value() == 3 + assert mock_conn.mock_calls == [ + call.describe_subnets(Filters=[{ + 'Name': 'status-code', 'Values': ['active'] + }]) + ] + def test_find_usages_vpn_gateways(self): response = result_fixtures.VPC.test_find_usages_vpn_gateways diff --git a/docs/source/limits.rst b/docs/source/limits.rst index 76b10121..3e3bec40 100644 --- a/docs/source/limits.rst +++ b/docs/source/limits.rst @@ -777,9 +777,9 @@ Daily sending quota |check| 200 VPC ---- -============================= =============== ======== ======= ==== -Limit Trusted Advisor Quotas API Default -============================= =============== ======== ======= ==== +===================================== =============== ======== ======= ==== +Limit Trusted Advisor Quotas API Default +===================================== =============== ======== ======= ==== Entries per route table |check| 50 Internet gateways |check| 5 NAT Gateways per AZ |check| 5 @@ -788,9 +788,10 @@ Network interfaces per Region |check| 5000 Route tables per VPC |check| 200 Rules per network ACL |check| 20 Subnets per VPC |check| 200 +Active VPC peering connections per VPC |check| 50 VPCs |check| 5 Virtual private gateways 5 -============================= =============== ======== ======= ==== +====================================== =============== ======== ======= ====