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',