From 0b4a4ce1260e65b0eb5cc90a0d2d45c36464a1f2 Mon Sep 17 00:00:00 2001 From: Gregory Thiemonge Date: Tue, 17 Dec 2019 17:52:52 +0100 Subject: [PATCH] Add amphora stats show API and CLI Added amphora_stats_show in the OctaviaAPI class. Added "amphora stats show" command in the CLI, with an optional "--listener " argument to display the statistics of a specific listener that runs in the amphora. Change-Id: I7ca43b55064e04e6d214a88eef5ed853090fbc34 --- octaviaclient/api/constants.py | 1 + octaviaclient/api/v2/octavia.py | 13 +++++ octaviaclient/osc/v2/amphora.py | 48 ++++++++++++++++ octaviaclient/tests/unit/api/test_octavia.py | 11 ++++ .../tests/unit/osc/v2/test_amphora.py | 57 +++++++++++++++++++ ...d-amphora-stats-show-48bc0fadf08f1057.yaml | 4 ++ setup.cfg | 1 + 7 files changed, 135 insertions(+) create mode 100644 releasenotes/notes/add-amphora-stats-show-48bc0fadf08f1057.yaml diff --git a/octaviaclient/api/constants.py b/octaviaclient/api/constants.py index ab39171..47151bf 100644 --- a/octaviaclient/api/constants.py +++ b/octaviaclient/api/constants.py @@ -46,6 +46,7 @@ BASE_AMPHORA_URL = BASE_OCTAVIA_ENDPOINT + "/amphorae" BASE_SINGLE_AMPHORA_URL = BASE_AMPHORA_URL + "/{uuid}" BASE_AMPHORA_CONFIGURE_URL = BASE_SINGLE_AMPHORA_URL + '/config' BASE_AMPHORA_FAILOVER_URL = BASE_SINGLE_AMPHORA_URL + '/failover' +BASE_AMPHORA_STATS_URL = BASE_SINGLE_AMPHORA_URL + '/stats' BASE_PROVIDER_URL = BASE_LBAAS_ENDPOINT + "/providers" BASE_PROVIDER_FLAVOR_CAPABILITY_URL = (BASE_LBAAS_ENDPOINT + diff --git a/octaviaclient/api/v2/octavia.py b/octaviaclient/api/v2/octavia.py index 1d883c5..be3a8be 100644 --- a/octaviaclient/api/v2/octavia.py +++ b/octaviaclient/api/v2/octavia.py @@ -773,6 +773,19 @@ class OctaviaAPI(api.BaseAPI): return response + def amphora_stats_show(self, amphora_id, **kwargs): + """Show the current statistics for an amphora + + :param string amphora_id: + ID of the amphora to show + :return: + A ``list`` of ``dict`` of the specified amphora's statistics + """ + url = const.BASE_AMPHORA_STATS_URL.format(uuid=amphora_id) + response = self._list(path=url, **kwargs) + + return response + def provider_list(self): """List all providers diff --git a/octaviaclient/osc/v2/amphora.py b/octaviaclient/osc/v2/amphora.py index 387162b..320a7a1 100644 --- a/octaviaclient/osc/v2/amphora.py +++ b/octaviaclient/osc/v2/amphora.py @@ -195,3 +195,51 @@ class FailoverAmphora(command.Command): amphora_show), res_id=amp_id ) + + +class ShowAmphoraStats(command.ShowOne): + """Shows the current statistics for an amphora.""" + + def get_parser(self, prog_name): + parser = super(ShowAmphoraStats, self).get_parser(prog_name) + + parser.add_argument( + '--listener', + metavar='', + help='Filter by listener (name or ID)', + ) + parser.add_argument( + 'amphora_id', + metavar='', + help='UUID of the amphora' + ) + + return parser + + def take_action(self, parsed_args): + rows = const.LOAD_BALANCER_STATS_ROWS + + listener_id = None + + if parsed_args.listener is not None: + attrs = v2_utils.get_listener_attrs( + self.app.client_manager, + parsed_args) + listener_id = attrs.pop('listener_id') + + data = self.app.client_manager.load_balancer.amphora_stats_show( + amphora_id=parsed_args.amphora_id + ) + + total_stats = { + key: 0 + for key in rows + } + for stats in data['amphora_stats']: + if listener_id is None or listener_id == stats['listener_id']: + for key in stats: + if key in rows: + total_stats[key] += stats[key] + + return (rows, (utils.get_dict_properties( + total_stats, rows, formatters={}))) diff --git a/octaviaclient/tests/unit/api/test_octavia.py b/octaviaclient/tests/unit/api/test_octavia.py index 19562f8..2a3772e 100644 --- a/octaviaclient/tests/unit/api/test_octavia.py +++ b/octaviaclient/tests/unit/api/test_octavia.py @@ -142,6 +142,7 @@ SINGLE_HM_UPDATE = {'healthmonitor': {'admin_state_up': False}} SINGLE_QT_RESP = {'quota': {'pool': -1}} SINGLE_QT_UPDATE = {'quota': {'pool': -1}} SINGLB_AMP_RESP = {'amphora': {'id': FAKE_AMP}} +SINGLE_AMP_STATS_RESP = {'bytes_in': '0'} SINGLE_PROVIDER_CAPABILITY_RESP = { 'flavor_capabilities': @@ -937,6 +938,16 @@ class TestLoadBalancer(TestOctaviaClient): self.api.amphora_configure, FAKE_AMP) + def test_stats_show_amphora(self): + self.requests_mock.register_uri( + 'GET', + FAKE_OCTAVIA_URL + 'amphorae/' + FAKE_AMP + '/stats', + json=SINGLE_AMP_STATS_RESP, + status_code=200 + ) + ret = self.api.amphora_stats_show(FAKE_AMP) + self.assertEqual(SINGLE_AMP_STATS_RESP, ret) + def test_failover_amphora(self): self.requests_mock.register_uri( 'PUT', diff --git a/octaviaclient/tests/unit/osc/v2/test_amphora.py b/octaviaclient/tests/unit/osc/v2/test_amphora.py index f6c0d0d..e1225c7 100644 --- a/octaviaclient/tests/unit/osc/v2/test_amphora.py +++ b/octaviaclient/tests/unit/osc/v2/test_amphora.py @@ -15,6 +15,7 @@ import copy import mock import osc_lib.tests.utils as osc_test_utils +from oslo_utils import uuidutils from octaviaclient.osc.v2 import amphora from octaviaclient.osc.v2 import constants @@ -213,3 +214,59 @@ class TestAmphoraFailover(TestAmphora): res_id=self._amp.id, sleep_time=mock.ANY, status_field='provisioning_status') + + +class TestAmphoraStatsShow(TestAmphora): + + def setUp(self): + super(TestAmphoraStatsShow, self).setUp() + # map fake listener_id to fake bytes_in counter + self.stats = { + uuidutils.generate_uuid(): 12, + uuidutils.generate_uuid(): 34, + } + amphora_stats_info = [ + {'listener_id': k, 'bytes_in': self.stats[k]} + for k in self.stats] + + self.api_mock.amphora_stats_show.return_value = { + 'amphora_stats': amphora_stats_info} + lb_client = self.app.client_manager + lb_client.load_balancer = self.api_mock + self.cmd = amphora.ShowAmphoraStats(self.app, None) + + def test_amphora_stats_show(self): + arglist = [self._amp.id] + verifylist = [ + ('amphora_id', self._amp.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.amphora_stats_show.assert_called_with( + amphora_id=self._amp.id) + + column_idx = columns.index('bytes_in') + total_bytes_in = sum(self.stats.values()) + self.assertEqual(data[column_idx], total_bytes_in) + + @mock.patch('octaviaclient.osc.v2.utils.get_listener_attrs') + def test_amphora_stats_show_with_listener_id(self, + mock_get_listener_attrs): + listener_id = list(self.stats)[0] + arglist = ['--listener', listener_id, self._amp.id] + verifylist = [ + ('amphora_id', self._amp.id), + ] + mock_get_listener_attrs.return_value = { + 'listener_id': listener_id + } + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.api_mock.amphora_stats_show.assert_called_with( + amphora_id=self._amp.id) + + column_idx = columns.index('bytes_in') + bytes_in = self.stats[listener_id] + self.assertEqual(data[column_idx], bytes_in) diff --git a/releasenotes/notes/add-amphora-stats-show-48bc0fadf08f1057.yaml b/releasenotes/notes/add-amphora-stats-show-48bc0fadf08f1057.yaml new file mode 100644 index 0000000..7c9cb7c --- /dev/null +++ b/releasenotes/notes/add-amphora-stats-show-48bc0fadf08f1057.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds support for querying the amphora statistics. diff --git a/setup.cfg b/setup.cfg index 64d59c1..8fd9045 100644 --- a/setup.cfg +++ b/setup.cfg @@ -84,6 +84,7 @@ openstack.load_balancer.v2 = loadbalancer_amphora_show = octaviaclient.osc.v2.amphora:ShowAmphora loadbalancer_amphora_configure = octaviaclient.osc.v2.amphora:ConfigureAmphora loadbalancer_amphora_failover = octaviaclient.osc.v2.amphora:FailoverAmphora + loadbalancer_amphora_stats_show = octaviaclient.osc.v2.amphora:ShowAmphoraStats loadbalancer_provider_list = octaviaclient.osc.v2.provider:ListProvider loadbalancer_provider_capability_list = octaviaclient.osc.v2.provider:ListProviderFlavorCapability loadbalancer_flavorprofile_create = octaviaclient.osc.v2.flavorprofile:CreateFlavorProfile