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

Add Network interfaces per Region limit #379

Merged
merged 4 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions awslimitchecker/services/vpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@

logger = logging.getLogger(__name__)

DEFAULT_ENI_LIMIT = 350


class _VpcService(_AwsService):

Expand All @@ -71,6 +73,7 @@ def find_usage(self):
self._find_usage_gateways()
self._find_usage_nat_gateways(subnet_to_az)
self._find_usages_vpn_gateways()
self._find_usage_network_interfaces()
self._have_usage = True
logger.debug("Done checking usage.")

Expand Down Expand Up @@ -210,6 +213,20 @@ def _find_usages_vpn_gateways(self):
aws_type='AWS::EC2::VPNGateway'
)

def _find_usage_network_interfaces(self):
"""find usage of network interfaces"""
enis = paginate_dict(
self.conn.describe_network_interfaces,
alc_marker_path=['NextToken'],
alc_data_path=['NetworkInterfaces'],
alc_marker_param='NextToken'
)

self.limits['Network interfaces per Region']._add_current_usage(
len(enis),
aws_type='AWS::EC2::NetworkInterface'
)

def get_limits(self):
"""
Return all known limits for this service, as a dict of their names
Expand Down Expand Up @@ -307,9 +324,42 @@ def get_limits(self):
self.critical_threshold,
limit_type='AWS::EC2::VPNGateway'
)

limits['Network interfaces per Region'] = AwsLimit(
'Network interfaces per Region',
self,
DEFAULT_ENI_LIMIT,
self.warning_threshold,
self.critical_threshold,
limit_type='AWS::EC2::NetworkInterface'
)
self.limits = limits
return limits

def _update_limits_from_api(self):
"""
Query EC2's DescribeAccountAttributes API action and
update the network interface limit, as needed. Updates ``self.limits``.

More info on the network interface limit, from the docs:
'This limit is the greater of either the default limit (350) or your
On-Demand Instance limit multiplied by 5.
The default limit for On-Demand Instances is 20.'
"""
self.connect()
self.connect_resource()
logger.info("Querying EC2 DescribeAccountAttributes for limits")
attribs = self.conn.describe_account_attributes()
for attrib in attribs['AccountAttributes']:
if attrib['AttributeName'] == 'max-instances':
val = attrib['AttributeValues'][0]['AttributeValue']
if int(val) * 5 > DEFAULT_ENI_LIMIT:
limit_name = 'Network interfaces per Region'
self.limits[limit_name]._set_api_limit(int(val))
else:
continue
logger.debug("Done setting limits from API")

def required_iam_permissions(self):
"""
Return a list of IAM Actions required for this Service to function
Expand All @@ -326,4 +376,5 @@ def required_iam_permissions(self):
'ec2:DescribeSubnets',
'ec2:DescribeVpcs',
'ec2:DescribeVpnGateways',
'ec2:DescribeNetworkInterfaces',
]
98 changes: 98 additions & 0 deletions awslimitchecker/tests/services/result_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,104 @@ class VPC(object):
]
}

test_find_usage_network_interfaces = {
'NetworkInterfaces': [
{
'Association': {
'AllocationId': 'allocation-123',
'AssociationId': 'association-123',
'IpOwnerId': '123456789012',
'PublicDnsName': 'string',
'PublicIp': '50.0.0.0'
},
'Attachment': {
'AttachTime': datetime(2015, 1, 1),
'AttachmentId': 'eni-123',
'DeleteOnTermination': True,
'DeviceIndex': 123,
'InstanceId': 'i-123',
'InstanceOwnerId': '123456789012',
'Status': 'attaching'
},
'AvailabilityZone': 'us-east-2a',
'Description': 'NetworkInface',
'Groups': [
{
'GroupName': 'groupName',
'GroupId': 'sg-123'
},
],
'InterfaceType': 'interface',
'Ipv6Addresses': [],
'MacAddress': 'address',
'NetworkInterfaceId': 'eni-123',
'OwnerId': 'string',
'PrivateDnsName': 'string',
'PrivateIpAddress': 'string',
'PrivateIpAddresses': [
{
'Association': {
'AllocationId': 'allocation-123',
'AssociationId': 'association-123',
'IpOwnerId': '123456789012',
'PublicDnsName': 'string',
'PublicIp': '10.0.0.0'
},
'Primary': True,
'PrivateDnsName': 'string',
'PrivateIpAddress': 'string'
},
],
'RequesterId': '123456789012',
'RequesterManaged': True,
'SourceDestCheck': True,
'Status': 'available',
'SubnetId': 'subnet-123',
'TagSet': [
{
'Key': 'tagkey',
'Value': 'tagvalue'
},
],
'VpcId': 'string'
},
]
}

test_update_limits_from_api_high_max_instances = {
'ResponseMetadata': {
'HTTPStatusCode': 200,
'RequestId': '16b85906-ab0d-4134-b8bb-df3e6120c6c7'
},
'AccountAttributes': [
{
'AttributeName': 'max-instances',
'AttributeValues': [
{
'AttributeValue': '400'
}
]
}
]
}

test_update_limits_from_api_low_max_instances = {
'ResponseMetadata': {
'HTTPStatusCode': 200,
'RequestId': '16b85906-ab0d-4134-b8bb-df3e6120c6c7'
},
'AccountAttributes': [
{
'AttributeName': 'max-instances',
'AttributeValues': [
{
'AttributeValue': '50'
}
]
}
]
}


class RDS(object):
test_find_usage_instances = []
Expand Down
77 changes: 75 additions & 2 deletions awslimitchecker/tests/services/test_vpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

import sys
from awslimitchecker.tests.services import result_fixtures
from awslimitchecker.services.vpc import _VpcService
from awslimitchecker.services.vpc import _VpcService, DEFAULT_ENI_LIMIT

from botocore.exceptions import ClientError

Expand Down Expand Up @@ -81,6 +81,7 @@ def test_get_limits(self):
'Rules per network ACL',
'Route tables per VPC',
'Virtual private gateways',
'Network interfaces per Region',
])
for name, limit in res.items():
assert limit.service == cls
Expand Down Expand Up @@ -111,7 +112,8 @@ def test_find_usage(self):
_find_usage_route_tables=DEFAULT,
_find_usage_gateways=DEFAULT,
_find_usage_nat_gateways=DEFAULT,
_find_usages_vpn_gateways=DEFAULT
_find_usages_vpn_gateways=DEFAULT,
_find_usage_network_interfaces=DEFAULT,
) as mocks:
mocks['_find_usage_subnets'].return_value = sn
cls = _VpcService(21, 43)
Expand All @@ -128,6 +130,7 @@ def test_find_usage(self):
'_find_usage_route_tables',
'_find_usage_gateways',
'_find_usages_vpn_gateways',
'_find_usage_network_interfaces',
]:
assert mocks[x].mock_calls == [call()]
assert mocks['_find_usage_nat_gateways'].mock_calls == [call(sn)]
Expand Down Expand Up @@ -339,6 +342,75 @@ def test_find_usages_vpn_gateways(self):
]),
]

def test_find_usage_network_interfaces(self):
response = result_fixtures.VPC.test_find_usage_network_interfaces

mock_conn = Mock()
mock_conn.describe_network_interfaces.return_value = response

cls = _VpcService(21, 43)
cls.conn = mock_conn

cls._find_usage_network_interfaces()

assert len(cls.limits['Network interfaces per Region']
.get_current_usage()) == 1
assert cls.limits['Network interfaces per Region'].get_current_usage()[
0].get_value() == 1
assert mock_conn.mock_calls == [
call.describe_network_interfaces(),
]

def test_update_limits_from_api_high_max_instances(self):
fixtures = result_fixtures.VPC()
response = fixtures.test_update_limits_from_api_high_max_instances

mock_conn = Mock()
mock_client_conn = Mock()
mock_client_conn.describe_account_attributes.return_value = response

cls = _VpcService(21, 43)
cls.resource_conn = mock_conn
cls.conn = mock_client_conn
with patch('awslimitchecker.services.vpc.logger') as mock_logger:
cls._update_limits_from_api()
assert mock_conn.mock_calls == []
assert mock_client_conn.mock_calls == [
call.describe_account_attributes()
]
assert mock_logger.mock_calls == [
call.info("Querying EC2 DescribeAccountAttributes for limits"),
call.debug('Done setting limits from API')
]
assert cls.limits['Network interfaces per Region'].api_limit == 400
assert cls.limits['Network interfaces per Region'].get_limit() == 400

def test_update_limits_from_api_low_max_instances(self):
fixtures = result_fixtures.VPC()
response = fixtures.test_update_limits_from_api_low_max_instances

mock_conn = Mock()
mock_client_conn = Mock()
mock_client_conn.describe_account_attributes.return_value = response

cls = _VpcService(21, 43)
cls.resource_conn = mock_conn
cls.conn = mock_client_conn
with patch('awslimitchecker.services.vpc.logger') as mock_logger:
cls._update_limits_from_api()
assert mock_conn.mock_calls == []
assert mock_client_conn.mock_calls == [
call.describe_account_attributes()
]
assert mock_logger.mock_calls == [
call.info("Querying EC2 DescribeAccountAttributes for limits"),
call.debug('Done setting limits from API')
]

limit_name = 'Network interfaces per Region'
assert cls.limits[limit_name].api_limit is None
assert cls.limits[limit_name].get_limit() == DEFAULT_ENI_LIMIT

def test_required_iam_permissions(self):
cls = _VpcService(21, 43)
assert cls.required_iam_permissions() == [
Expand All @@ -348,4 +420,5 @@ def test_required_iam_permissions(self):
'ec2:DescribeSubnets',
'ec2:DescribeVpcs',
'ec2:DescribeVpnGateways',
'ec2:DescribeNetworkInterfaces',
]
Empty file added default)
Empty file.