From f9fd69e9c685d10197230d917b04a2ff71052eec Mon Sep 17 00:00:00 2001 From: Bartosz Zurkowski Date: Mon, 8 Oct 2018 13:02:51 +0200 Subject: [PATCH] Add detailed list for instances Currently, listing instances only allows to get basic information about entities. To get the details, one need to query instance "show" endpoint for each instance separately. This is inefficient and exposes API to a heavier load. There are use cases in which we want to obtain detailed information about all instances. In particular, in services integrating with Trove. For example, Vitrage project requires this information to build vertices and edges in the resource graph for RCA analysis. Change-Id: I33252cce41c27cc7302c860dde1f6448ecdf3991 Signed-off-by: Bartosz Zurkowski --- ...stance-detailed-list-e712dccf6c9091c0.yaml | 4 ++++ trove/common/api.py | 4 ++++ trove/common/policies/base.py | 1 + trove/common/policies/instances.py | 12 +++++++++- trove/instance/service.py | 24 ++++++++++++++++--- trove/instance/views.py | 5 ++-- trove/tests/api/instances.py | 19 +++++++++++++++ 7 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/add-instance-detailed-list-e712dccf6c9091c0.yaml diff --git a/releasenotes/notes/add-instance-detailed-list-e712dccf6c9091c0.yaml b/releasenotes/notes/add-instance-detailed-list-e712dccf6c9091c0.yaml new file mode 100644 index 0000000000..86715bb3f0 --- /dev/null +++ b/releasenotes/notes/add-instance-detailed-list-e712dccf6c9091c0.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Added ``/instances/detail`` endpoint to fetch list of instances with details. diff --git a/trove/common/api.py b/trove/common/api.py index 78b67227fa..5ff5ef7d0a 100644 --- a/trove/common/api.py +++ b/trove/common/api.py @@ -85,6 +85,10 @@ class API(wsgi.Router): controller=instance_resource, action="index", conditions={'method': ['GET']}) + mapper.connect("/{tenant_id}/instances/detail", + controller=instance_resource, + action="detail", + conditions={'method': ['GET']}) mapper.connect("/{tenant_id}/instances", controller=instance_resource, action="create", diff --git a/trove/common/policies/base.py b/trove/common/policies/base.py index 2ea4c62f6a..94d4831268 100644 --- a/trove/common/policies/base.py +++ b/trove/common/policies/base.py @@ -15,6 +15,7 @@ from oslo_policy import policy PATH_BASE = '/v1.0/{account_id}' PATH_INSTANCES = PATH_BASE + '/instances' +PATH_INSTANCES_DETAIL = PATH_INSTANCES + '/detail' PATH_INSTANCE = PATH_INSTANCES + '/{instance_id}' PATH_INSTANCE_ACTION = PATH_INSTANCE + '/action' PATH_USERS = PATH_INSTANCE + '/users' diff --git a/trove/common/policies/instances.py b/trove/common/policies/instances.py index 3aeeb3151a..1ce4b74385 100644 --- a/trove/common/policies/instances.py +++ b/trove/common/policies/instances.py @@ -13,7 +13,7 @@ from oslo_policy import policy from trove.common.policies.base import ( - PATH_INSTANCES, PATH_INSTANCE, PATH_INSTANCE_ACTION) + PATH_INSTANCES, PATH_INSTANCES_DETAIL, PATH_INSTANCE, PATH_INSTANCE_ACTION) rules = [ @@ -57,6 +57,16 @@ rules = [ 'method': 'GET' } ]), + policy.DocumentedRuleDefault( + name='instance:detail', + check_str='rule:admin_or_owner', + description='List database instances with details.', + operations=[ + { + 'path': PATH_INSTANCES_DETAIL, + 'method': 'GET' + } + ]), policy.DocumentedRuleDefault( name='instance:show', check_str='rule:admin_or_owner', diff --git a/trove/instance/service.py b/trove/instance/service.py index 9d1f4b57e4..184122f494 100644 --- a/trove/instance/service.py +++ b/trove/instance/service.py @@ -198,13 +198,31 @@ class InstanceController(wsgi.Controller): LOG.debug("req : '%s'\n\n", req) context = req.environ[wsgi.CONTEXT_KEY] policy.authorize_on_tenant(context, 'instance:index') + instances = self._get_instances(req, instance_view=views.InstanceView) + return wsgi.Result(instances, 200) + + def detail(self, req, tenant_id): + """Return all instances with details.""" + LOG.info("Listing database instances with details for tenant '%s'", + tenant_id) + LOG.debug("req : '%s'\n\n", req) + context = req.environ[wsgi.CONTEXT_KEY] + policy.authorize_on_tenant(context, 'instance:detail') + instances = self._get_instances(req, + instance_view=views.InstanceDetailView) + return wsgi.Result(instances, 200) + + def _get_instances(self, req, instance_view): + context = req.environ[wsgi.CONTEXT_KEY] clustered_q = req.GET.get('include_clustered', '').lower() include_clustered = clustered_q == 'true' - servers, marker = models.Instances.load(context, include_clustered) - view = views.InstancesView(servers, req=req) + instances, marker = models.Instances.load(context, include_clustered) + view = views.InstancesView(instances, + item_view=instance_view, + req=req) paged = pagination.SimplePaginatedDataView(req.url, 'instances', view, marker) - return wsgi.Result(paged.data(), 200) + return paged.data() def backups(self, req, tenant_id, id): """Return all backups for the specified instance.""" diff --git a/trove/instance/views.py b/trove/instance/views.py index aeeb807be9..c0dab012ab 100644 --- a/trove/instance/views.py +++ b/trove/instance/views.py @@ -162,8 +162,9 @@ class InstanceDetailView(InstanceView): class InstancesView(object): """Shows a list of SimpleInstance objects.""" - def __init__(self, instances, req=None): + def __init__(self, instances, item_view=InstanceView, req=None): self.instances = instances + self.item_view = item_view self.req = req def data(self): @@ -174,7 +175,7 @@ class InstancesView(object): return {'instances': data} def data_for_instance(self, instance): - view = InstanceView(instance, req=self.req) + view = self.item_view(instance, req=self.req) return view.data()['instance'] diff --git a/trove/tests/api/instances.py b/trove/tests/api/instances.py index adc42f0691..1715ee708b 100644 --- a/trove/tests/api/instances.py +++ b/trove/tests/api/instances.py @@ -1179,6 +1179,25 @@ class TestInstanceListing(object): check.datastore() check.volume() + @test + def test_detailed_list(self): + allowed_attrs = ['created', 'databases', 'flavor', 'hostname', 'id', + 'links', 'name', 'status', 'updated', 'ip', + 'datastore', 'fault', 'region'] + if VOLUME_SUPPORT: + allowed_attrs.append('volume') + instances = dbaas.instances.list(detailed=True) + assert_equal(200, dbaas.last_http_code) + for instance in instances: + instance_dict = instance._info + with CheckInstance(instance_dict) as check: + check.contains_allowed_attrs(instance_dict, allowed_attrs, + msg="Instance Detailed Index") + check.flavor() + check.datastore() + check.volume() + check.used_volume() + @test def test_get_instance(self): allowed_attrs = ['created', 'databases', 'flavor', 'hostname', 'id',