diff --git a/awslimitchecker/__main__.py b/awslimitchecker/__main__.py new file mode 100644 index 00000000..f7b5c80a --- /dev/null +++ b/awslimitchecker/__main__.py @@ -0,0 +1,20 @@ +# The idea of this file is to enable running the package with +# `python -m awslimitchecker` or `python -m awslimitchecker [args]`. +# This enables debugging the package when installed in editable mode +# (`pip install -e .`) and running the package from the command line. + +import sys + +from . import runner + + +def main(args=None): + if args is None: + args = sys.argv[1:] + # Parse arguments and run your package's main functionality + print("Running awslimitchecker with arguments:", args) + runner.console_entry_point() + + +if __name__ == "__main__": + main() diff --git a/awslimitchecker/limit.py b/awslimitchecker/limit.py index 65e054cf..b8226ce7 100644 --- a/awslimitchecker/limit.py +++ b/awslimitchecker/limit.py @@ -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) diff --git a/awslimitchecker/runner.py b/awslimitchecker/runner.py index df61babf..38c07de5 100644 --- a/awslimitchecker/runner.py +++ b/awslimitchecker/runner.py @@ -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 @@ -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 = "" + if data.quotas_limit: + limit = int(data.quotas_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 diff --git a/awslimitchecker/tests/test_limit.py b/awslimitchecker/tests/test_limit.py index be7dd1c3..cbbd4a47 100644 --- a/awslimitchecker/tests/test_limit.py +++ b/awslimitchecker/tests/test_limit.py @@ -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) @@ -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) diff --git a/awslimitchecker/tests/test_runner.py b/awslimitchecker/tests/test_runner.py index e80f472d..fb385141 100644 --- a/awslimitchecker/tests/test_runner.py +++ b/awslimitchecker/tests/test_runner.py @@ -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', '-', ''], + ['SvcBar/barlimit1', '-', '11', '-', ''], + ['SvcFoo/foo limit3', '-', '33', '-', ''] + ],) def test_one_service(self, capsys): limits = { @@ -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', '-', '']], + ) class TestCheckThresholds(RunnerTester): diff --git a/setup.py b/setup.py index e598b380..e05715c5 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,8 @@ 'python-dateutil', 'versionfinder>=0.1.1', 'pytz', - 'urllib3' + 'urllib3', + 'tabulate>=0.9.0', ] classifiers = [