From f5ea8ac085b46affe2220843470f071693aabed7 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Fri, 16 Jun 2017 01:03:09 -0700 Subject: [PATCH] Add statistics to V2 API This patch implements stats to the Octavia API. It also corrects the path for load balancer status. Change-Id: I9405857ab4f62664daca13562cc07ee8e1a519c7 Co-Authored-By: Michael Johnson --- octavia/api/v2/controllers/listener.py | 39 +++++++ octavia/api/v2/controllers/load_balancer.py | 51 ++++++-- octavia/api/v2/types/health_monitor.py | 4 +- octavia/api/v2/types/listener.py | 29 ++++- octavia/api/v2/types/load_balancer.py | 37 ++++-- octavia/api/v2/types/member.py | 6 +- octavia/api/v2/types/pool.py | 14 +-- octavia/tests/functional/api/v2/base.py | 11 ++ .../tests/functional/api/v2/test_listener.py | 105 +++++++++++++++++ .../functional/api/v2/test_load_balancer.py | 109 +++++++++++++++++- 10 files changed, 369 insertions(+), 36 deletions(-) diff --git a/octavia/api/v2/controllers/listener.py b/octavia/api/v2/controllers/listener.py index 98976ae2f2..3b494def09 100644 --- a/octavia/api/v2/controllers/listener.py +++ b/octavia/api/v2/controllers/listener.py @@ -28,6 +28,7 @@ from octavia.api.v2.types import listener as listener_types from octavia.common import constants from octavia.common import data_models from octavia.common import exceptions +from octavia.common import stats from octavia.db import api as db_api from octavia.db import prepare as db_prepare @@ -328,3 +329,41 @@ class ListenersController(base.BaseController): self.repositories.listener.update( lock_session, db_listener.id, provisioning_status=constants.ERROR) + + @pecan.expose() + def _lookup(self, id, *remainder): + """Overridden pecan _lookup method for custom routing. + + Currently it checks if this was a stats request and routes + the request to the StatsController. + """ + if id and len(remainder) and remainder[0] == 'stats': + return StatisticsController(listener_id=id), remainder[1:] + + +class StatisticsController(base.BaseController, stats.StatsMixin): + RBAC_TYPE = constants.RBAC_LISTENER + + def __init__(self, listener_id): + super(StatisticsController, self).__init__() + self.id = listener_id + + @wsme_pecan.wsexpose(listener_types.StatisticsRootResponse, wtypes.text, + status_code=200) + def get(self): + context = pecan.request.context.get('octavia_context') + db_listener = self._get_db_listener(context.session, self.id) + if not db_listener: + LOG.info("Listener %s not found.", id) + raise exceptions.NotFound( + resource=data_models.Listener._name(), + id=id) + + self._auth_validate_action(context, db_listener.project_id, + constants.RBAC_GET_STATS) + + listener_stats = self.get_listener_stats(context.session, self.id) + + result = self._convert_db_to_type( + listener_stats, listener_types.ListenerStatisticsResponse) + return listener_types.StatisticsRootResponse(stats=result) diff --git a/octavia/api/v2/controllers/load_balancer.py b/octavia/api/v2/controllers/load_balancer.py index 989b73dd31..53839739bd 100644 --- a/octavia/api/v2/controllers/load_balancer.py +++ b/octavia/api/v2/controllers/load_balancer.py @@ -29,6 +29,7 @@ from octavia.api.v2.types import load_balancer as lb_types from octavia.common import constants from octavia.common import data_models from octavia.common import exceptions +from octavia.common import stats from octavia.common import utils import octavia.common.validate as validate from octavia.db import api as db_api @@ -401,18 +402,24 @@ class LoadBalancersController(base.BaseController): Currently it checks if this was a statuses request and routes the request to the StatusesController. """ - if id and len(remainder) and remainder[0] == 'statuses': - return StatusesController(lb_id=id), remainder[1:] + if id and len(remainder) and (remainder[0] == 'status' or + remainder[0] == 'stats'): + controller = remainder[0] + remainder = remainder[1:] + if controller == 'status': + return StatusController(lb_id=id), remainder + elif controller == 'stats': + return StatisticsController(lb_id=id), remainder -class StatusesController(base.BaseController): +class StatusController(base.BaseController): RBAC_TYPE = constants.RBAC_LOADBALANCER def __init__(self, lb_id): - super(StatusesController, self).__init__() + super(StatusController, self).__init__() self.id = lb_id - @wsme_pecan.wsexpose(lb_types.StatusesRootResponse, wtypes.text, + @wsme_pecan.wsexpose(lb_types.StatusRootResponse, wtypes.text, status_code=200) def get(self): context = pecan.request.context.get('octavia_context') @@ -427,6 +434,34 @@ class StatusesController(base.BaseController): constants.RBAC_GET_STATUS) result = self._convert_db_to_type( - load_balancer, lb_types.LoadBalancerStatusesResponse) - result = lb_types.StatusesResponse(loadbalancer=result) - return lb_types.StatusesRootResponse(statuses=result) + load_balancer, lb_types.LoadBalancerStatusResponse) + result = lb_types.StatusResponse(loadbalancer=result) + return lb_types.StatusRootResponse(statuses=result) + + +class StatisticsController(base.BaseController, stats.StatsMixin): + RBAC_TYPE = constants.RBAC_LOADBALANCER + + def __init__(self, lb_id): + super(StatisticsController, self).__init__() + self.id = lb_id + + @wsme_pecan.wsexpose(lb_types.StatisticsRootResponse, wtypes.text, + status_code=200) + def get(self): + context = pecan.request.context.get('octavia_context') + load_balancer = self._get_db_lb(context.session, self.id) + if not load_balancer: + LOG.info("Load balancer %s not found.", id) + raise exceptions.NotFound( + resource=data_models.LoadBalancer._name(), + id=id) + + self._auth_validate_action(context, load_balancer.project_id, + constants.RBAC_GET_STATS) + + lb_stats = self.get_loadbalancer_stats(context.session, self.id) + + result = self._convert_db_to_type( + lb_stats, lb_types.LoadBalancerStatisticsResponse) + return lb_types.StatisticsRootResponse(stats=result) diff --git a/octavia/api/v2/types/health_monitor.py b/octavia/api/v2/types/health_monitor.py index 14293425e7..31700c4d32 100644 --- a/octavia/api/v2/types/health_monitor.py +++ b/octavia/api/v2/types/health_monitor.py @@ -157,8 +157,8 @@ class HealthMonitorSingleCreate(BaseHealthMonitorType): admin_state_up = wtypes.wsattr(bool, default=True) -class HealthMonitorStatusesResponse(BaseHealthMonitorType): - """Defines which attributes are to be shown on statuses response.""" +class HealthMonitorStatusResponse(BaseHealthMonitorType): + """Defines which attributes are to be shown on status response.""" id = wtypes.wsattr(wtypes.UuidType()) name = wtypes.wsattr(wtypes.StringType()) type = wtypes.wsattr(wtypes.text) diff --git a/octavia/api/v2/types/listener.py b/octavia/api/v2/types/listener.py index c48562b46f..afff963299 100644 --- a/octavia/api/v2/types/listener.py +++ b/octavia/api/v2/types/listener.py @@ -156,20 +156,20 @@ class ListenerSingleCreate(BaseListenerType): wtypes.DictType(str, wtypes.StringType(max_length=255))) -class ListenerStatusesResponse(BaseListenerType): - """Defines which attributes are to be shown on statuses response.""" +class ListenerStatusResponse(BaseListenerType): + """Defines which attributes are to be shown on status response.""" id = wtypes.wsattr(wtypes.UuidType()) name = wtypes.wsattr(wtypes.StringType()) operating_status = wtypes.wsattr(wtypes.StringType()) provisioning_status = wtypes.wsattr(wtypes.StringType()) - pools = wtypes.wsattr([pool.PoolStatusesResponse]) + pools = wtypes.wsattr([pool.PoolStatusResponse]) @classmethod def from_data_model(cls, data_model, children=False): - listener = super(ListenerStatusesResponse, cls).from_data_model( + listener = super(ListenerStatusResponse, cls).from_data_model( data_model, children=children) - pool_model = pool.PoolStatusesResponse + pool_model = pool.PoolStatusResponse listener.pools = [ pool_model.from_data_model(i) for i in data_model.pools] @@ -177,3 +177,22 @@ class ListenerStatusesResponse(BaseListenerType): listener.name = "" return listener + + +class ListenerStatisticsResponse(BaseListenerType): + """Defines which attributes are to show on stats response.""" + bytes_in = wtypes.wsattr(wtypes.IntegerType()) + bytes_out = wtypes.wsattr(wtypes.IntegerType()) + active_connections = wtypes.wsattr(wtypes.IntegerType()) + total_connections = wtypes.wsattr(wtypes.IntegerType()) + request_errors = wtypes.wsattr(wtypes.IntegerType()) + + @classmethod + def from_data_model(cls, data_model, children=False): + result = super(ListenerStatisticsResponse, cls).from_data_model( + data_model, children=children) + return result + + +class StatisticsRootResponse(types.BaseType): + stats = wtypes.wsattr(ListenerStatisticsResponse) diff --git a/octavia/api/v2/types/load_balancer.py b/octavia/api/v2/types/load_balancer.py index 852efcd5d6..4af4a4dfe8 100644 --- a/octavia/api/v2/types/load_balancer.py +++ b/octavia/api/v2/types/load_balancer.py @@ -141,19 +141,19 @@ class LoadBalancerRootPUT(types.BaseType): loadbalancer = wtypes.wsattr(LoadBalancerPUT) -class LoadBalancerStatusesResponse(BaseLoadBalancerType): - """Defines which attributes are to be shown on statuses response.""" +class LoadBalancerStatusResponse(BaseLoadBalancerType): + """Defines which attributes are to be shown on status response.""" id = wtypes.wsattr(wtypes.UuidType()) name = wtypes.wsattr(wtypes.StringType()) operating_status = wtypes.wsattr(wtypes.StringType()) provisioning_status = wtypes.wsattr(wtypes.StringType()) - listeners = wtypes.wsattr([listener.ListenerStatusesResponse]) + listeners = wtypes.wsattr([listener.ListenerStatusResponse]) @classmethod def from_data_model(cls, data_model, children=False): - result = super(LoadBalancerStatusesResponse, cls).from_data_model( + result = super(LoadBalancerStatusResponse, cls).from_data_model( data_model, children=children) - listener_model = listener.ListenerStatusesResponse + listener_model = listener.ListenerStatusResponse result.listeners = [ listener_model.from_data_model(i) for i in data_model.listeners] if not result.name: @@ -162,9 +162,28 @@ class LoadBalancerStatusesResponse(BaseLoadBalancerType): return result -class StatusesResponse(wtypes.Base): - loadbalancer = wtypes.wsattr(LoadBalancerStatusesResponse) +class StatusResponse(wtypes.Base): + loadbalancer = wtypes.wsattr(LoadBalancerStatusResponse) -class StatusesRootResponse(types.BaseType): - statuses = wtypes.wsattr(StatusesResponse) +class StatusRootResponse(types.BaseType): + statuses = wtypes.wsattr(StatusResponse) + + +class LoadBalancerStatisticsResponse(BaseLoadBalancerType): + """Defines which attributes are to show on stats response.""" + bytes_in = wtypes.wsattr(wtypes.IntegerType()) + bytes_out = wtypes.wsattr(wtypes.IntegerType()) + active_connections = wtypes.wsattr(wtypes.IntegerType()) + total_connections = wtypes.wsattr(wtypes.IntegerType()) + request_errors = wtypes.wsattr(wtypes.IntegerType()) + + @classmethod + def from_data_model(cls, data_model, children=False): + result = super(LoadBalancerStatisticsResponse, cls).from_data_model( + data_model, children=children) + return result + + +class StatisticsRootResponse(types.BaseType): + stats = wtypes.wsattr(LoadBalancerStatisticsResponse) diff --git a/octavia/api/v2/types/member.py b/octavia/api/v2/types/member.py index cda1c2831b..cb5de591a1 100644 --- a/octavia/api/v2/types/member.py +++ b/octavia/api/v2/types/member.py @@ -114,8 +114,8 @@ class MemberSingleCreate(BaseMemberType): subnet_id = wtypes.wsattr(wtypes.UuidType()) -class MemberStatusesResponse(BaseMemberType): - """Defines which attributes are to be shown on statuses response.""" +class MemberStatusResponse(BaseMemberType): + """Defines which attributes are to be shown on status response.""" id = wtypes.wsattr(wtypes.UuidType()) name = wtypes.wsattr(wtypes.StringType()) operating_status = wtypes.wsattr(wtypes.StringType()) @@ -125,7 +125,7 @@ class MemberStatusesResponse(BaseMemberType): @classmethod def from_data_model(cls, data_model, children=False): - member = super(MemberStatusesResponse, cls).from_data_model( + member = super(MemberStatusResponse, cls).from_data_model( data_model, children=children) if not member.name: diff --git a/octavia/api/v2/types/pool.py b/octavia/api/v2/types/pool.py index fb2d200b78..624e95516c 100644 --- a/octavia/api/v2/types/pool.py +++ b/octavia/api/v2/types/pool.py @@ -166,25 +166,25 @@ class PoolSingleCreate(BasePoolType): members = wtypes.wsattr([member.MemberSingleCreate]) -class PoolStatusesResponse(BasePoolType): - """Defines which attributes are to be shown on statuses response.""" +class PoolStatusResponse(BasePoolType): + """Defines which attributes are to be shown on status response.""" id = wtypes.wsattr(wtypes.UuidType()) name = wtypes.wsattr(wtypes.StringType()) provisioning_status = wtypes.wsattr(wtypes.StringType()) operating_status = wtypes.wsattr(wtypes.StringType()) health_monitor = wtypes.wsattr( - health_monitor.HealthMonitorStatusesResponse) - members = wtypes.wsattr([member.MemberStatusesResponse]) + health_monitor.HealthMonitorStatusResponse) + members = wtypes.wsattr([member.MemberStatusResponse]) @classmethod def from_data_model(cls, data_model, children=False): - pool = super(PoolStatusesResponse, cls).from_data_model( + pool = super(PoolStatusResponse, cls).from_data_model( data_model, children=children) - member_model = member.MemberStatusesResponse + member_model = member.MemberStatusResponse if data_model.health_monitor: pool.health_monitor = ( - health_monitor.HealthMonitorStatusesResponse.from_data_model( + health_monitor.HealthMonitorStatusResponse.from_data_model( data_model.health_monitor)) pool.members = [ member_model.from_data_model(i) for i in data_model.members] diff --git a/octavia/tests/functional/api/v2/base.py b/octavia/tests/functional/api/v2/base.py index fa3db2838c..85671dc07b 100644 --- a/octavia/tests/functional/api/v2/base.py +++ b/octavia/tests/functional/api/v2/base.py @@ -189,6 +189,17 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase): request_errors=0) return db_ls.to_dict() + def create_listener_stats_dynamic(self, listener_id, amphora_id, + bytes_in=0, bytes_out=0, + active_connections=0, + total_connections=0, request_errors=0): + db_ls = self.listener_stats_repo.create( + db_api.get_session(), listener_id=listener_id, + amphora_id=amphora_id, bytes_in=bytes_in, + bytes_out=bytes_out, active_connections=active_connections, + total_connections=total_connections, request_errors=request_errors) + return db_ls.to_dict() + def create_amphora(self, amphora_id, loadbalancer_id, **optionals): # We need to default these values in the request. opts = {'compute_id': uuidutils.generate_uuid(), diff --git a/octavia/tests/functional/api/v2/test_listener.py b/octavia/tests/functional/api/v2/test_listener.py index 228acacc9f..0fd8c6c6a4 100644 --- a/octavia/tests/functional/api/v2/test_listener.py +++ b/octavia/tests/functional/api/v2/test_listener.py @@ -14,6 +14,7 @@ # under the License. import mock +import random from oslo_config import cfg from oslo_config import fixture as oslo_fixture @@ -1128,3 +1129,107 @@ class TestListener(base.BaseAPITest): 'insert_headers': {'X-Forwarded-Four': 'true'}} body = self._build_body(lb_listener) self.post(self.LISTENERS_PATH, body, status=400) + + def _getStats(self, listener_id): + res = self.get(self.LISTENER_PATH.format( + listener_id=listener_id + "/stats")) + return res.json.get('stats') + + def test_statistics(self): + lb = self.create_load_balancer( + uuidutils.generate_uuid()).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + ls = self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9), + request_errors=random.randint(1, 9)) + + response = self._getStats(li['id']) + self.assertEqual(ls['bytes_in'], response['bytes_in']) + self.assertEqual(ls['bytes_out'], response['bytes_out']) + self.assertEqual(ls['total_connections'], + response['total_connections']) + self.assertEqual(ls['active_connections'], + response['active_connections']) + self.assertEqual(ls['request_errors'], + response['request_errors']) + + def test_statistics_authorized(self): + project_id = uuidutils.generate_uuid() + lb = self.create_load_balancer( + uuidutils.generate_uuid(), + project_id=project_id).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + ls = self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9), + request_errors=random.randint(1, 9)) + self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF)) + auth_strategy = self.conf.conf.api_settings.get('auth_strategy') + self.conf.config(group='api_settings', auth_strategy=constants.TESTING) + + with mock.patch.object(octavia.common.context.Context, 'project_id', + project_id): + override_credentials = { + 'service_user_id': None, + 'user_domain_id': None, + 'is_admin_project': True, + 'service_project_domain_id': None, + 'service_project_id': None, + 'roles': ['load-balancer_member'], + 'user_id': None, + 'is_admin': False, + 'service_user_domain_id': None, + 'project_domain_id': None, + 'service_roles': [], + 'project_id': project_id} + with mock.patch( + "oslo_context.context.RequestContext.to_policy_values", + return_value=override_credentials): + response = self._getStats(li['id']) + self.conf.config(group='api_settings', auth_strategy=auth_strategy) + + self.assertEqual(ls['bytes_in'], response['bytes_in']) + self.assertEqual(ls['bytes_out'], response['bytes_out']) + self.assertEqual(ls['total_connections'], + response['total_connections']) + self.assertEqual(ls['active_connections'], + response['active_connections']) + self.assertEqual(ls['request_errors'], + response['request_errors']) + + def test_statistics_not_authorized(self): + lb = self.create_load_balancer( + uuidutils.generate_uuid()).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9), + request_errors=random.randint(1, 9)) + self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF)) + auth_strategy = self.conf.conf.api_settings.get('auth_strategy') + self.conf.config(group='api_settings', auth_strategy=constants.TESTING) + with mock.patch.object(octavia.common.context.Context, 'project_id', + uuidutils.generate_uuid()): + res = self.get(self.LISTENER_PATH.format( + listener_id=li['id'] + "/stats"), status=403) + self.conf.config(group='api_settings', auth_strategy=auth_strategy) + self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json) diff --git a/octavia/tests/functional/api/v2/test_load_balancer.py b/octavia/tests/functional/api/v2/test_load_balancer.py index 973f31bcee..be16d317d8 100644 --- a/octavia/tests/functional/api/v2/test_load_balancer.py +++ b/octavia/tests/functional/api/v2/test_load_balancer.py @@ -13,6 +13,7 @@ # under the License. import copy +import random import mock from oslo_config import cfg @@ -1827,7 +1828,7 @@ class TestLoadBalancerGraph(base.BaseAPITest): self.post(self.LBS_PATH, body) def _getStatus(self, lb_id): - res = self.get(self.LB_PATH.format(lb_id=lb_id + "/statuses")) + res = self.get(self.LB_PATH.format(lb_id=lb_id + "/status")) return res.json.get('statuses').get('loadbalancer') def test_statuses(self): @@ -2122,7 +2123,111 @@ class TestLoadBalancerGraph(base.BaseAPITest): with mock.patch.object(octavia.common.context.Context, 'project_id', uuidutils.generate_uuid()): - res = self.get(self.LB_PATH.format(lb_id=lb['id'] + "/statuses"), + res = self.get(self.LB_PATH.format(lb_id=lb['id'] + "/status"), status=403) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json) + + def _getStats(self, lb_id): + res = self.get(self.LB_PATH.format(lb_id=lb_id + "/stats")) + return res.json.get('stats') + + def test_statistics(self): + lb = self.create_load_balancer( + uuidutils.generate_uuid()).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + ls = self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9), + request_errors=random.randint(1, 9)) + + response = self._getStats(lb['id']) + self.assertEqual(ls['bytes_in'], response['bytes_in']) + self.assertEqual(ls['bytes_out'], response['bytes_out']) + self.assertEqual(ls['total_connections'], + response['total_connections']) + self.assertEqual(ls['active_connections'], + response['active_connections']) + self.assertEqual(ls['request_errors'], + response['request_errors']) + + def test_statistics_authorized(self): + project_id = uuidutils.generate_uuid() + lb = self.create_load_balancer( + uuidutils.generate_uuid(), + project_id=project_id).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + ls = self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9), + request_errors=random.randint(1, 9)) + + self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF)) + auth_strategy = self.conf.conf.api_settings.get('auth_strategy') + self.conf.config(group='api_settings', auth_strategy=constants.TESTING) + + with mock.patch.object(octavia.common.context.Context, 'project_id', + project_id): + override_credentials = { + 'service_user_id': None, + 'user_domain_id': None, + 'is_admin_project': True, + 'service_project_domain_id': None, + 'service_project_id': None, + 'roles': ['load-balancer_member'], + 'user_id': None, + 'is_admin': False, + 'service_user_domain_id': None, + 'project_domain_id': None, + 'service_roles': [], + 'project_id': project_id} + with mock.patch( + "oslo_context.context.RequestContext.to_policy_values", + return_value=override_credentials): + response = self._getStats(lb['id']) + self.conf.config(group='api_settings', auth_strategy=auth_strategy) + + self.assertEqual(ls['bytes_in'], response['bytes_in']) + self.assertEqual(ls['bytes_out'], response['bytes_out']) + self.assertEqual(ls['total_connections'], + response['total_connections']) + self.assertEqual(ls['active_connections'], + response['active_connections']) + self.assertEqual(ls['request_errors'], + response['request_errors']) + + def test_statistics_not_authorized(self): + lb = self.create_load_balancer( + uuidutils.generate_uuid()).get('loadbalancer') + self.set_lb_status(lb['id']) + li = self.create_listener( + constants.PROTOCOL_HTTP, 80, lb.get('id')).get('listener') + amphora = self.create_amphora(uuidutils.generate_uuid(), lb['id']) + self.create_listener_stats_dynamic( + listener_id=li.get('id'), + amphora_id=amphora.id, + bytes_in=random.randint(1, 9), + bytes_out=random.randint(1, 9), + total_connections=random.randint(1, 9)) + self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF)) + auth_strategy = self.conf.conf.api_settings.get('auth_strategy') + self.conf.config(group='api_settings', auth_strategy=constants.TESTING) + with mock.patch.object(octavia.common.context.Context, 'project_id', + uuidutils.generate_uuid()): + res = self.get(self.LB_PATH.format(lb_id=lb['id'] + "/stats"), + status=403) + + self.conf.config(group='api_settings', auth_strategy=auth_strategy) + self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json)