Skip to content

Commit

Permalink
Improve report by adding limit and percentage - #384
Browse files Browse the repository at this point in the history
The idea here is to improve the visualization of the reporting
by adding not only the usage, but the limit and how close to the
limit it is.

Also, to make it easier to read, print it all in a table format.

Example before:

```
EC2/All F Spot Instance Requests                                          0
EC2/All G Spot Instance Requests                                          0
EC2/All Inf Spot Instance Requests                                        0
EC2/All P Spot Instance Requests                                          0
EC2/All Standard (A, C, D, H, I, M, R, T, Z) Spot Instance Requests       0
EC2/All X Spot Instance Requests                                          0
EC2/Elastic IP addresses (EIPs)                                           0
EC2/Max active spot fleets per region                                     0
EC2/Max launch specifications per spot fleet                              <unknown>
EC2/Max target capacity for all spot fleets in region                     0
EC2/Max target capacity per spot fleet                                    <unknown>
EC2/Rules per VPC security group                                          max: sg-0648da97fdeebf980=102 (sg-0df2ad106237174ea=1, sg-07e5e4f72cea0dfe5=1, sg-0b9911c871252d8e7=1, sg-0d055747ec7c4140e=1, sg-09551693a5250b7f4=1, sg-0781c381e1b5d378a=1, sg-0fd94d543ec6cf4ee=1, sg-05e714614de7941cd=1, sg-03cc3b695a6366ffd=1, sg-083eae2676c6f8b92=1, sg-0dc2ee2f21505bf2c=1, sg-0e1729f5b07df6416=1, sg-0de5f793ac0da38e7=1, sg-0d79228bbb3f97b6d=1, sg-0c7bcbb474d8afbf5=1, sg-0d6aa6e6209d923b5=1, sg-086b6c19c0860460b=1, sg-0c56e7af849a12622=1, sg-086a3c13d414aef8b=1, sg-0853c55cd9df708de=1, sg-0e1ed9e1ceca720a1=1, sg-099ef683e4d35e2ee=1, sg-0adc28eb7faa40ecf=1, sg-0dabf1e2c30f5998f=1, sg-06aedf016149fbe14=1, sg-0f9148df50a48a291=1, sg-006e95a00de69f1b8=1, sg-0f9f697bf9ef106a5=1,
```

Example after:

```
┌──────────────────────────────────────────────────────────────────────────┬───────────────────────┬───────────┬───────────┬───────────┐
│ Service Limit                                                            │ Resource              │   Usage # │ Usage %   │ Limit     │
├──────────────────────────────────────────────────────────────────────────┼───────────────────────┼───────────┼───────────┼───────────┤
│ EC2/All F Spot Instance Requests                                         │ -                     │         0 │ 0 %       │ 128       │
│ EC2/All G Spot Instance Requests                                         │ -                     │         0 │ -         │ <unknown> │
│ EC2/All Inf Spot Instance Requests                                       │ -                     │         0 │ 0 %       │ 64        │
│ EC2/All P Spot Instance Requests                                         │ -                     │         0 │ -         │ <unknown> │
│ EC2/All Standard (A, C, D, H, I, M, R, T, Z) Spot Instance Requests      │ -                     │         0 │ 0 %       │ 640       │
│ EC2/All X Spot Instance Requests                                         │ -                     │         0 │ 0 %       │ 128       │
│ EC2/Elastic IP addresses (EIPs)                                          │ -                     │         0 │ -         │ <unknown> │
│ EC2/Max active spot fleets per region                                    │ -                     │         0 │ -         │ <unknown> │
│ EC2/Max target capacity for all spot fleets in region                    │ -                     │         0 │ -         │ <unknown> │
│ EC2/Rules per VPC security group                                         │ sg-058d83cc259d4e69c  │         3 │ 2 %       │ 120       │
│ EC2/Rules per VPC security group                                         │ sg-0783065c4d2b78c83  │         3 │ 2 %       │ 120       │
│ EC2/Rules per VPC security group                                         │ sg-0df2ad106237174ea  │         1 │ 1 %       │ 120       │
│ EC2/Rules per VPC security group                                         │ sg-07e5e4f72cea0dfe5  │         1 │ 1 %       │ 120       │
│ EC2/Rules per VPC security group                                         │ sg-0b9911c871252d8e7  │         1 │ 1 %       │ 120       │
│ EC2/Rules per VPC security group                                         │ sg-0229a5ed0b61ce2bd  │         2 │ 2 %       │ 120       │
│ EC2/Rules per VPC security group                                         │ sg-0b25ba5186ce99117  │         2 │ 2 %       │ 120       │
```
  • Loading branch information
derrix060 committed Jun 28, 2024
1 parent 9e359d5 commit de44146
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 67 deletions.
3 changes: 1 addition & 2 deletions awslimitchecker/limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,8 @@ class instance. Return True if usage is within thresholds, or false if
limit = u.get_maximum() or self.get_limit()
if limit is None or limit == 0:
continue
pct = (usage / (limit * 1.0)) * 100
pct = (usage / limit) * 100
if crit_int is not None and usage >= crit_int:
self._criticals.append(u)
all_ok = False
elif pct >= crit_pct:
self._criticals.append(u)
Expand Down
41 changes: 31 additions & 10 deletions awslimitchecker/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,20 @@
##############################################################################
"""

import sys
import argparse
import logging
import json
import boto3
import logging
import sys
import time

import boto3
import tabulate

from .alerts import AlertProvider
from .checker import AwsLimitChecker
from .utils import StoreKeyValuePair, dict2cols, issue_string_tuple
from .limit import SOURCE_TA, SOURCE_API, SOURCE_QUOTAS
from .limit import SOURCE_API, SOURCE_QUOTAS, SOURCE_TA
from .metrics import MetricsProvider
from .alerts import AlertProvider
from .utils import StoreKeyValuePair, dict2cols, issue_string_tuple

try:
from urllib.parse import urlparse
Expand Down Expand Up @@ -319,12 +321,31 @@ def show_usage(self):
service=self.service_name, use_ta=(not self.skip_ta))
limits = self.checker.get_limits(
service=self.service_name, use_ta=(not self.skip_ta))
data = {}
headers = ['Service Limit', 'Resource', 'Usage #', 'Usage %', 'Limit']
table = []
for svc in sorted(limits.keys()):
for lim in sorted(limits[svc].keys()):
data["{s}/{l}".format(s=svc, l=lim)] = '{v}'.format(
v=limits[svc][lim].get_current_usage_str())
print(dict2cols(data))
data = limits[svc][lim]
for usage in data.get_current_usage():
service = svc
limit_name = lim
resource = usage.resource_id or '-'
limit = "<unknown>"
if data.quotas_limit:
limit = int(data.get_limit())
use = usage.value
use_percent = "-"
if isinstance(limit, (int, float)):
use_percent = "{:.0f} %".format((use / limit) * 100)
table.append([
f"{service}/{limit_name}",
resource,
str(use),
use_percent,
str(limit),
])
print(tabulate.tabulate(
table, headers=headers, tablefmt="simple_outline"))

def check_thresholds(self, metrics=None):
have_warn = False
Expand Down
72 changes: 46 additions & 26 deletions awslimitchecker/tests/test_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,23 +602,33 @@ def test_int_warn(self):
assert mock_get_limit.mock_calls == [call(), call(), call()]

def test_int_warn_crit(self):
limit = AwsLimit('limitname', self.mock_svc, 100, 1, 2)
u1 = AwsLimitUsage(limit, 4, resource_id='foo4bar')
u2 = AwsLimitUsage(limit, 1, resource_id='foo3bar')
u3 = AwsLimitUsage(limit, 7, resource_id='foo2bar')
limit = AwsLimit(
name='limitname',
service=self.mock_svc,
default_limit=10,
def_warning_threshold=40,
def_critical_threshold=60,
)
u1 = AwsLimitUsage(
limit=limit,
value=4,
resource_id='foo4bar',
)
u2 = AwsLimitUsage(
limit=limit,
value=1,
resource_id='foo3bar',
)
u3 = AwsLimitUsage(
limit=limit,
value=7,
resource_id='foo2bar',
)
limit._current_usage = [u1, u2, u3]
with patch('awslimitchecker.limit.AwsLimit.'
'_get_thresholds') as mock_get_thresh:
with patch('awslimitchecker.limit.AwsLimit.get_'
'limit') as mock_get_limit:
mock_get_thresh.return_value = (4, 40, 6, 80)
mock_get_limit.return_value = 100
res = limit.check_thresholds()
res = limit.check_thresholds()
assert res is False
assert limit._warnings == [u1]
assert limit._criticals == [u3]
assert mock_get_thresh.mock_calls == [call()]
assert mock_get_limit.mock_calls == [call(), call(), call()]

def test_pct_crit(self):
limit = AwsLimit('limitname', self.mock_svc, 100, 1, 2)
Expand All @@ -640,23 +650,33 @@ def test_pct_crit(self):
assert mock_get_limit.mock_calls == [call(), call(), call()]

def test_int_crit(self):
limit = AwsLimit('limitname', self.mock_svc, 100, 1, 2)
u1 = AwsLimitUsage(limit, 9, resource_id='foo4bar')
u2 = AwsLimitUsage(limit, 3, resource_id='foo3bar')
u3 = AwsLimitUsage(limit, 95, resource_id='foo2bar')
limit = AwsLimit(
name='limitname',
service=self.mock_svc,
default_limit=10,
def_warning_threshold=60,
def_critical_threshold=80,
)
u1 = AwsLimitUsage(
limit=limit,
value=9,
resource_id='foo4bar',
)
u2 = AwsLimitUsage(
limit=limit,
value=3,
resource_id='foo3bar',
)
u3 = AwsLimitUsage(
limit=limit,
value=95,
resource_id='foo2bar',
)
limit._current_usage = [u1, u2, u3]
with patch('awslimitchecker.limit.AwsLimit.'
'_get_thresholds') as mock_get_thresh:
with patch('awslimitchecker.limit.AwsLimit.get_'
'limit') as mock_get_limit:
mock_get_thresh.return_value = (6, 40, 8, 80)
mock_get_limit.return_value = 100
res = limit.check_thresholds()
res = limit.check_thresholds()
assert res is False
assert limit._warnings == []
assert limit._criticals == [u1, u3]
assert mock_get_thresh.mock_calls == [call()]
assert mock_get_limit.mock_calls == [call(), call(), call()]

def test_pct_warn_crit(self):
limit = AwsLimit('limitname', self.mock_svc, 100, 1, 2)
Expand Down
49 changes: 21 additions & 28 deletions awslimitchecker/tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,22 +769,18 @@ def test_default(self, capsys):
mock_checker = Mock(spec_set=AwsLimitChecker)
mock_checker.get_limits.return_value = limits
self.cls.checker = mock_checker
with patch('awslimitchecker.runner.dict2cols') as mock_d2c:
mock_d2c.return_value = 'd2cval'
with patch('awslimitchecker.runner.tabulate') as mock_tabulate:
self.cls.show_usage()
out, err = capsys.readouterr()
assert out == 'd2cval\n'
assert mock_checker.mock_calls == [
call.find_usage(service=None, use_ta=True),
call.get_limits(service=None, use_ta=True)
]
assert mock_d2c.mock_calls == [
call({
'SvcBar/bar limit2': '22',
'SvcBar/barlimit1': '11',
'SvcFoo/foo limit3': '33',
})
]
assert mock_checker.mock_calls == [
call.find_usage(service=None, use_ta=True),
call.get_limits(service=None, use_ta=True)
]
assert len(mock_tabulate.method_calls) == 1
assert mock_tabulate.method_calls[0].args == ([
['SvcBar/bar limit2', '-', '22', '-', '<unknown>'],
['SvcBar/barlimit1', '-', '11', '-', '<unknown>'],
['SvcFoo/foo limit3', '-', '33', '-', '<unknown>']
],)

def test_one_service(self, capsys):
limits = {
Expand All @@ -796,20 +792,17 @@ def test_one_service(self, capsys):
self.cls.checker = mock_checker
self.cls.service_name = ['SvcFoo']
self.cls.skip_ta = True
with patch('awslimitchecker.runner.dict2cols') as mock_d2c:
mock_d2c.return_value = 'd2cval'
with patch('awslimitchecker.runner.tabulate') as mock_tabulate:
self.cls.show_usage()
out, err = capsys.readouterr()
assert out == 'd2cval\n'
assert mock_checker.mock_calls == [
call.find_usage(service=['SvcFoo'], use_ta=False),
call.get_limits(service=['SvcFoo'], use_ta=False)
]
assert mock_d2c.mock_calls == [
call({
'SvcFoo/foo limit3': '33',
})
]
out, err = capsys.readouterr()
assert mock_checker.mock_calls == [
call.find_usage(service=['SvcFoo'], use_ta=False),
call.get_limits(service=['SvcFoo'], use_ta=False)
]
assert len(mock_tabulate.method_calls) == 1
assert mock_tabulate.method_calls[0].args == (
[['SvcFoo/foo limit3', '-', '33', '-', '<unknown>']],
)


class TestCheckThresholds(RunnerTester):
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
'python-dateutil',
'versionfinder>=0.1.1',
'pytz',
'urllib3'
'urllib3',
'tabulate>=0.9.0',
]

classifiers = [
Expand Down

0 comments on commit de44146

Please sign in to comment.