diff --git a/gnocchiclient/tests/functional/test_resource.py b/gnocchiclient/tests/functional/test_resource.py index a96e861..335d069 100644 --- a/gnocchiclient/tests/functional/test_resource.py +++ b/gnocchiclient/tests/functional/test_resource.py @@ -19,6 +19,7 @@ from gnocchiclient.tests.functional import base class ResourceClientTest(base.ClientTestBase): RESOURCE_ID = str(uuid.uuid4()) + RESOURCE_ID2 = str(uuid.uuid4()) PROJECT_ID = str(uuid.uuid4()) def details_multiple(self, output_lines, with_label=False): @@ -96,17 +97,45 @@ class ResourceClientTest(base.ClientTestBase): # Search result = self.gnocchi('resource', - params="search generic --query 'project_id=%s'" % - self.PROJECT_ID) + params=("search generic " + "--query 'project_id=%s'" + ) % self.PROJECT_ID) resource_list = self.parser.listing(result)[0] self.assertEqual(self.RESOURCE_ID, resource_list["id"]) self.assertEqual(self.PROJECT_ID, resource_list["project_id"]) self.assertEqual(resource["started_at"], resource_list["started_at"]) + # CREATE 2 + result = self.gnocchi( + 'resource', params=("create generic " + "-a id:%s " + "-a project_id:%s" + ) % (self.RESOURCE_ID2, self.PROJECT_ID)) + resource2 = self.details_multiple(result)[0] + self.assertEqual(self.RESOURCE_ID2, resource2["id"]) + self.assertEqual(self.PROJECT_ID, resource2["project_id"]) + self.assertNotEqual('None', resource2["started_at"]) + + # Search + limit + short + result = self.gnocchi('resource', + params=("search generic " + "--query 'project_id=%s' " + "--sort started_at:asc " + "--marker %s " + "--limit 1" + ) % (self.PROJECT_ID, self.RESOURCE_ID)) + resource_limit = self.parser.listing(result)[0] + self.assertEqual(self.RESOURCE_ID2, resource_limit["id"]) + self.assertEqual(self.PROJECT_ID, resource_limit["project_id"]) + self.assertEqual(resource2["started_at"], resource_limit["started_at"]) + # DELETE result = self.gnocchi('resource', params="delete %s" % self.RESOURCE_ID) self.assertEqual("", result) + result = self.gnocchi('resource', + params="delete %s" % self.RESOURCE_ID2) + self.assertEqual("", result) # GET FAIL result = self.gnocchi('resource', @@ -124,5 +153,6 @@ class ResourceClientTest(base.ClientTestBase): # LIST EMPTY result = self.gnocchi('resource', params="list generic") - self.assertNotIn(self.RESOURCE_ID, - [r['id'] for r in self.parser.listing(result)]) + resource_ids = [r['id'] for r in self.parser.listing(result)] + self.assertNotIn(self.RESOURCE_ID, resource_ids) + self.assertNotIn(self.RESOURCE_ID2, resource_ids) diff --git a/gnocchiclient/v1/resource.py b/gnocchiclient/v1/resource.py index ca964ab..f37c35f 100644 --- a/gnocchiclient/v1/resource.py +++ b/gnocchiclient/v1/resource.py @@ -12,12 +12,30 @@ # under the License. from oslo_serialization import jsonutils +from six.moves.urllib import parse as urllib_parse from gnocchiclient.v1 import base +def _get_pagination_options(details=False, history=False, + limit=None, marker=None, sorts=None): + options = [] + if details: + options.append("details=true") + if history: + options.append("history=true") + if limit: + options.append("limit=%d" % limit) + if marker: + options.append("marker=%s" % urllib_parse.quote(marker)) + for sort in sorts or []: + options.append("sort=%s" % urllib_parse.quote(sort)) + return "&".join(options) + + class ResourceManager(base.Manager): - def list(self, resource_type="generic", details=False, history=False): + def list(self, resource_type="generic", details=False, history=False, + limit=None, marker=None, sorts=None): """List resources :param resource_type: Type of the resource @@ -26,11 +44,16 @@ class ResourceManager(base.Manager): :type details: bool :param history: Show the history of resources :type history: bool + :param limit: maximum number of resources to return + :type limit: int + :param marker: the last item of the previous page; we returns the next + results after this value. + :param sorts: list of resource attributes to order by. (example + ["user_id:desc-nullslast", "project_id:asc"] + :type sorts: list of str """ - details = "true" if details else "false" - history = "true" if history else "false" - url = self.client._build_url("resource/%s?details=%s&history=%s" % ( - resource_type, details, history)) + qs = _get_pagination_options(details, history, limit, marker, sorts) + url = self.client._build_url("resource/%s?%s" % (resource_type, qs)) return self.client.api.get(url).json() def get(self, resource_type, resource_id, history=False): @@ -48,7 +71,8 @@ class ResourceManager(base.Manager): resource_type, resource_id, history)) return self.client.api.get(url).json() - def history(self, resource_type, resource_id, details=False): + def history(self, resource_type, resource_id, details=False, + limit=None, marker=None, sorts=None): """Get a resource :param resource_type: Type of the resource @@ -57,10 +81,17 @@ class ResourceManager(base.Manager): :type resource_id: str :param details: Show all attributes of resources :type details: bool + :param limit: maximum number of resources to return + :type limit: int + :param marker: the last item of the previous page; we returns the next + results after this value. + :param sorts: list of resource attributes to order by. (example + ["user_id:desc-nullslast", "project_id:asc"] + :type sorts: list of str """ - details = "true" if details else "false" - url = self.client._build_url("resource/%s/%s/history?details=%s" % ( - resource_type, resource_id, details)) + qs = _get_pagination_options(details, False, limit, marker, sorts) + url = self.client._build_url("resource/%s/%s/history?%s" % ( + resource_type, resource_id, qs)) return self.client.api.get(url).json() def create(self, resource_type, resource): @@ -103,7 +134,7 @@ class ResourceManager(base.Manager): self.client.api.delete(url) def search(self, resource_type="generic", request=None, details=False, - history=False): + history=False, limit=None, marker=None, sorts=None): """List resources :param resource_type: Type of the resource @@ -114,6 +145,13 @@ class ResourceManager(base.Manager): :type details: bool :param history: Show the history of resources :type history: bool + :param limit: maximum number of resources to return + :type limit: int + :param marker: the last item of the previous page; we returns the next + results after this value. + :param sorts: list of resource attributes to order by. (example + ["user_id:desc-nullslast", "project_id:asc"] + :type sorts: list of str See Gnocchi REST API documentation for the format of *request dictionary* @@ -121,11 +159,9 @@ class ResourceManager(base.Manager): """ request = request or {} - details = "true" if details else "false" - history = "true" if history else "false" + qs = _get_pagination_options(details, False, limit, marker, sorts) url = self.client._build_url( - "/search/resource/%s?details=%s&history=%s" % ( - resource_type, details, history)) + "/search/resource/%s?%s" % (resource_type, qs)) return self.client.api.post( url, headers={'Content-Type': "application/json"}, data=jsonutils.dumps(request)).json() diff --git a/gnocchiclient/v1/resourcecli.py b/gnocchiclient/v1/resourcecli.py index 61aa4b1..519845b 100644 --- a/gnocchiclient/v1/resourcecli.py +++ b/gnocchiclient/v1/resourcecli.py @@ -33,6 +33,15 @@ class CliResourceList(lister.Lister): if history: parser.add_argument("--history", action='store_true', help="Show history of the resources"), + parser.add_argument("--limit", type=int, metavar="", + help=("Number of resources to return " + "(Default is server default)")) + parser.add_argument("--marker", metavar="", + help=("Last item of the previous listing. " + "Return the next results after this value")) + parser.add_argument("--sort", action="append", metavar="", + help=("Sort of resource attribute ", + "(example: user_id:desc-nullslast")) parser.add_argument("resource_type", default="generic", nargs='?', @@ -42,10 +51,21 @@ class CliResourceList(lister.Lister): def take_action(self, parsed_args): resources = self.app.client.resource.list( resource_type=parsed_args.resource_type, - details=parsed_args.details, - history=parsed_args.history) + **self._get_pagination_options(parsed_args)) return self.COLS, [self._resource2tuple(r) for r in resources] + @staticmethod + def _get_pagination_options(parsed_args): + options = dict( + details=parsed_args.details, + sorts=parsed_args.sort, + limit=parsed_args.limit, + marker=parsed_args.marker) + + if hasattr(parsed_args, 'history'): + options['history'] = parsed_args.history + return options + @classmethod def _resource2tuple(cls, resource): return tuple([resource[k] for k in cls.COLS]) @@ -63,7 +83,7 @@ class CliResourceHistory(CliResourceList): resources = self.app.client.resource.history( resource_type=parsed_args.resource_type, resource_id=parsed_args.resource_id, - details=parsed_args.details) + **self._get_pagination_options(parsed_args)) return self.COLS, [self._resource2tuple(r) for r in resources] @@ -77,9 +97,8 @@ class CliResourceSearch(CliResourceList): def take_action(self, parsed_args): resources = self.app.client.resource.search( resource_type=parsed_args.resource_type, - details=parsed_args.details, - history=parsed_args.history, - request=utils.search_query_builder(parsed_args.query)) + request=utils.search_query_builder(parsed_args.query), + **self._get_pagination_options(parsed_args)) return self.COLS, [self._resource2tuple(r) for r in resources]