diff --git a/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py b/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py index 082f4b72..ce41b0f2 100644 --- a/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py +++ b/octavia_tempest_plugin/services/load_balancer/v2/listener_client.py @@ -13,6 +13,8 @@ # under the License. # +import json + from tempest import config from octavia_tempest_plugin.services.load_balancer.v2 import base_client @@ -291,3 +293,53 @@ class ListenerClient(base_client.BaseLBaaSClient): """ return self._delete_obj(obj_id=listener_id, ignore_errors=ignore_errors) + + def get_listener_stats(self, listener_id, query_params=None, + return_object_only=True): + """Get listener statistics. + + :param listener_id: The listener ID to query. + :param query_params: The optional query parameters to append to the + request. Ex. fields=id&fields=name + :param return_object_only: If True, the response returns the object + inside the root tag. False returns the full + response from the API. + :raises AssertionError: if the expected_code isn't a valid http success + response code + :raises BadRequest: If a 400 response code is received + :raises Conflict: If a 409 response code is received + :raises Forbidden: If a 403 response code is received + :raises Gone: If a 410 response code is received + :raises InvalidContentType: If a 415 response code is received + :raises InvalidHTTPResponseBody: The response body wasn't valid JSON + :raises InvalidHttpSuccessCode: if the read code isn't an expected + http success code + :raises NotFound: If a 404 response code is received + :raises NotImplemented: If a 501 response code is received + :raises OverLimit: If a 413 response code is received and over_limit is + not in the response body + :raises RateLimitExceeded: If a 413 response code is received and + over_limit is in the response body + :raises ServerFault: If a 500 response code is received + :raises Unauthorized: If a 401 response code is received + :raises UnexpectedContentType: If the content-type of the response + isn't an expect type + :raises UnexpectedResponseCode: If a response code above 400 is + received and it doesn't fall into any + of the handled checks + :raises UnprocessableEntity: If a 422 response code is received and + couldn't be parsed + :returns: A listener statistics object. + """ + if query_params: + request_uri = '{0}/{1}/stats?{2}'.format(self.uri, listener_id, + query_params) + else: + request_uri = '{0}/{1}/stats'.format(self.uri, listener_id) + + response, body = self.get(request_uri) + self.expected_success(200, response.status) + if return_object_only: + return json.loads(body.decode('utf-8'))['stats'] + else: + return json.loads(body.decode('utf-8')) diff --git a/octavia_tempest_plugin/tests/api/v2/test_listener.py b/octavia_tempest_plugin/tests/api/v2/test_listener.py index 38da0aef..b68ae552 100644 --- a/octavia_tempest_plugin/tests/api/v2/test_listener.py +++ b/octavia_tempest_plugin/tests/api/v2/test_listener.py @@ -764,3 +764,76 @@ class ListenerAPITest(test_base.LoadBalancerBaseTest): const.ACTIVE, CONF.load_balancer.check_interval, CONF.load_balancer.check_timeout) + + @decorators.idempotent_id('6f14a6c1-945e-43bc-8215-410c8a5edb25') + def test_listener_show_stats(self): + """Tests listener show statistics API. + + * Create a listener. + * Validates that other accounts cannot see the stats for the + * listener. + * Show listener statistics. + * Validate the show reflects the expected values. + """ + listener_name = data_utils.rand_name("lb_member_listener1-stats") + listener_description = data_utils.arbitrary_string(size=255) + + listener_kwargs = { + const.NAME: listener_name, + const.DESCRIPTION: listener_description, + const.ADMIN_STATE_UP: True, + const.PROTOCOL: const.HTTP, + const.PROTOCOL_PORT: 84, + const.LOADBALANCER_ID: self.lb_id, + const.CONNECTION_LIMIT: 200, + } + + listener = self.mem_listener_client.create_listener(**listener_kwargs) + self.addCleanup( + self.mem_listener_client.cleanup_listener, + listener[const.ID], + lb_client=self.mem_lb_client, lb_id=self.lb_id) + + waiters.wait_for_status( + self.mem_lb_client.show_loadbalancer, self.lb_id, + const.PROVISIONING_STATUS, const.ACTIVE, + CONF.load_balancer.build_interval, + CONF.load_balancer.build_timeout) + listener = waiters.wait_for_status( + self.mem_listener_client.show_listener, + listener[const.ID], const.PROVISIONING_STATUS, + const.ACTIVE, + CONF.load_balancer.build_interval, + CONF.load_balancer.build_timeout) + if not CONF.load_balancer.test_with_noop: + listener = waiters.wait_for_status( + self.mem_listener_client.show_listener, + listener[const.ID], const.OPERATING_STATUS, + const.ONLINE, + CONF.load_balancer.build_interval, + CONF.load_balancer.build_timeout) + + # Test that a user, without the load balancer member role, cannot + # use this command + if CONF.load_balancer.RBAC_test_type == const.ADVANCED: + self.assertRaises( + exceptions.Forbidden, + self.os_primary.listener_client.get_listener_stats, + listener[const.ID]) + + # Test that a different user, with the load balancer role, cannot see + # the listener stats + if not CONF.load_balancer.RBAC_test_type == const.NONE: + member2_client = self.os_roles_lb_member2.listener_client + self.assertRaises(exceptions.Forbidden, + member2_client.get_listener_stats, + listener[const.ID]) + + stats = self.mem_listener_client.get_listener_stats(listener[const.ID]) + + self.assertEqual(5, len(stats)) + self.assertEqual(0, stats[const.ACTIVE_CONNECTIONS]) + self.assertEqual(0, stats[const.BYTES_IN]) + self.assertEqual(0, stats[const.BYTES_OUT]) + self.assertEqual(0, stats[const.REQUEST_ERRORS]) + self.assertEqual(0, stats[const.TOTAL_CONNECTIONS]) diff --git a/releasenotes/notes/add-listener-stats-api-test-88947cf5e6ae9cae.yaml b/releasenotes/notes/add-listener-stats-api-test-88947cf5e6ae9cae.yaml new file mode 100644 index 00000000..1737aa23 --- /dev/null +++ b/releasenotes/notes/add-listener-stats-api-test-88947cf5e6ae9cae.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Added the listener stats service client and API test.