]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: add rbd list sorting support
authorPere Diaz Bou <pdiazbou@redhat.com>
Mon, 4 Jul 2022 14:57:43 +0000 (16:57 +0200)
committerPere Diaz Bou <pdiazbou@redhat.com>
Thu, 14 Jul 2022 15:27:28 +0000 (17:27 +0200)
Support sorting with name, pool name and namespace

Signed-off-by: Pere Diaz Bou <pdiazbou@redhat.com>
(cherry picked from commit 3c308804d5a7a5e96842e3234edbf6ad42509e93)

Conflicts:
src/pybind/mgr/dashboard/services/rbd.py

remove _rbd_image_stat_parent function because it isn't used in pacific.

qa/tasks/mgr/dashboard/test_rbd.py
src/pybind/mgr/dashboard/controllers/rbd.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-fetch-data-context.ts
src/pybind/mgr/dashboard/services/rbd.py

index 4e0367fd5be77b299394faa66a40e1a8d7f220f0..0bb4b49817e454f1b6900e6aba375c3a595bef18 100644 (file)
@@ -13,7 +13,7 @@ class RbdTest(DashboardTestCase):
 
     @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['create', 'update', 'delete']}])
     def test_read_access_permissions(self):
-        self._get('/api/block/image')
+        self._get('/api/block/image?offset=0&limit=5&search=&sort=%3Cname')
         self.assertStatus(403)
         self.get_image('pool', None, 'image')
         self.assertStatus(403)
index d26fcfa7292b12b2207b25a105c4473216e0989f..943a0936720526919a2f58fb4cbd230f6e5587b2 100644 (file)
@@ -80,14 +80,14 @@ class Rbd(RESTController):
     ALLOW_DISABLE_FEATURES = {"exclusive-lock", "object-map", "fast-diff", "deep-flatten",
                               "journaling"}
 
-    def _rbd_list(self, pool_name=None, offset=0, limit=5, search=''):
+    def _rbd_list(self, pool_name=None, offset=0, limit=5, search='', sort=''):
         if pool_name:
             pools = [pool_name]
         else:
             pools = [p['pool_name'] for p in CephService.get_pool_list('rbd')]
 
         images, num_total_images = RbdService.rbd_pool_list(
-            pools, offset=offset, limit=limit, search=search)
+            pools, offset=offset, limit=limit, search=search, sort=sort)
         cherrypy.response.headers['X-Total-Count'] = num_total_images
         pool_result = {}
         for i, image in enumerate(images):
@@ -111,8 +111,8 @@ class Rbd(RESTController):
                  responses={200: RBD_SCHEMA})
     @RESTController.MethodMap(version=APIVersion(2, 0))  # type: ignore
     def list(self, pool_name=None, offset: int = 0, limit: int = 5,
-             search: str = ''):
-        return self._rbd_list(pool_name, offset=offset, limit=limit, search=search)
+             search: str = '', sort: str = ''):
+        return self._rbd_list(pool_name, offset=offset, limit=limit, search=search, sort=sort)
 
     @handle_rbd_error()
     @handle_rados_error('pool')
index 71d71218486959b9c4d42676c598194ae32a804d..983f39ed9be523e7ef53bad5d6f1841cc1a4f558 100644 (file)
@@ -243,13 +243,11 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
       {
         name: $localize`Pool`,
         prop: 'pool_name',
-        sortable: false,
         flexGrow: 2
       },
       {
         name: $localize`Namespace`,
         prop: 'namespace',
-        sortable: false,
         flexGrow: 2
       },
       {
@@ -375,6 +373,9 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
     if (context !== null) {
       this.tableContext = context;
     }
+    if (this.tableContext == null) {
+      this.tableContext = new CdTableFetchDataContext(() => undefined);
+    }
     return this.rbdService.list(this.tableContext?.toParams());
   }
 
index dc51614ffedd7968f0ca2071e782354e76dafd2c..f5ab8615ac65d1985250267dadd11be1633492af 100644 (file)
@@ -59,7 +59,7 @@ describe('RbdService', () => {
     /* tslint:disable:no-empty */
     const context = new CdTableFetchDataContext(() => {});
     service.list(context.toParams()).subscribe();
-    const req = httpTesting.expectOne('api/block/image?offset=0&limit=10');
+    const req = httpTesting.expectOne('api/block/image?offset=0&limit=10?search=&sort=<name');
     expect(req.request.method).toBe('GET');
   });
 
index 5476430613005377250e80a14509907f0c309d81..285b94b4a2cc0805ff5ae95ca1c1b2c52a5d5d0d 100644 (file)
                  [footerHeight]="footer ? 'auto' : 0"
                  [count]="count"
                  [externalPaging]="serverSide"
+                 [externalSorting]="serverSide"
                  [limit]="userConfig.limit > 0 ? userConfig.limit : undefined"
                  [offset]="userConfig.offset >= 0 ? userConfig.offset : 0"
                  (page)="changePage($event)"
index d60dc28517f7ec01fa848938a284ce8758a1b0b2..c8941684a3670275fddfbc5aff7b09a203f54e80 100644 (file)
@@ -653,6 +653,10 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O
       context.pageInfo.offset = this.userConfig.offset;
       context.pageInfo.limit = this.userConfig.limit;
       context.search = this.userConfig.search;
+      if (this.userConfig.sorts?.length) {
+        const sort = this.userConfig.sorts[0];
+        context.sort = `${sort.dir == 'desc' ? '<' : '>'}${sort.prop}`;
+      }
       this.fetchData.emit(context);
       this.updating = true;
     }
@@ -791,6 +795,9 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O
 
   changeSorting({ sorts }: any) {
     this.userConfig.sorts = sorts;
+    if (this.serverSide) {
+      this.reloadData();
+    }
   }
 
   onClearSearch() {
index 56efadeb2baeab64d5b899588774c26b7e1c70eb..7f50bb7f1ec9107943ad2416cd468ade16559295 100644 (file)
@@ -16,6 +16,7 @@ export class CdTableFetchDataContext {
   error: Function;
   pageInfo: PageInfo = new PageInfo();
   search = '';
+  sort = '>name';
 
   constructor(error: () => void) {
     this.error = error;
@@ -25,14 +26,18 @@ export class CdTableFetchDataContext {
     if (this.pageInfo.limit === null) {
       this.pageInfo.limit = 0;
     }
-    if (this.search === null) {
+    if (!this.search) {
       this.search = '';
     }
+    if (!this.sort) {
+      this.sort = '>name';
+    }
     return new HttpParams({
       fromObject: {
         offset: String(this.pageInfo.offset * this.pageInfo.limit),
         limit: String(this.pageInfo.limit),
-        search: this.search
+        search: this.search,
+        sort: this.sort
       }
     });
   }
index a0ee3ef70e75f188273553f4e3c6e15447004a27..95444cea68260c210010c930ae6a13f3b3590492 100644 (file)
@@ -11,6 +11,7 @@ import rbd
 
 from .. import mgr
 from ..exceptions import DashboardException
+from ..plugins.ttl_cache import ttl_cache
 from .ceph_service import CephService
 
 try:
@@ -391,6 +392,7 @@ class RbdService(object):
             return stat
 
     @classmethod
+    @ttl_cache(10)
     def _rbd_image_refs(cls, ioctx):
         rbd_inst = rbd.RBD()
         return rbd_inst.list2(ioctx)
@@ -437,7 +439,7 @@ class RbdService(object):
         return joint_refs
 
     @classmethod
-    def rbd_pool_list(cls, pool_names: List[str], namespace=None, offset=0, limit=0, search=''):
+    def rbd_pool_list(cls, pool_names: List[str], namespace=None, offset=0, limit=0, search='', sort=''):
         offset = int(offset)
         limit = int(limit)
         # let's use -1 to denotate we want ALL images for now. Iscsi currently gathers
@@ -451,12 +453,23 @@ class RbdService(object):
         for ref in refs:
             if search in ref['name']:
                 image_refs.append(ref)
+            elif search in ref['pool']:
+                image_refs.append(ref)
+            elif search in ref['namespace']:
+                image_refs.append(ref)
 
         result = []
         end = offset + limit
+        descending = sort[0] == '<'
+        sort_by = sort[1:]
+        if sort_by == 'pool_name':
+            sort_by = 'pool'
+        if sort_by not in ['name', 'pool', 'namespace']:
+            sort_by = 'name'
         if limit == -1:
             end = len(image_refs)
-        for image_ref in sorted(image_refs, key=lambda v: v['name'])[offset:end]:
+        for image_ref in sorted(image_refs, key=lambda v: v[sort_by],
+                                reverse=descending)[offset:end]:
             with mgr.rados.open_ioctx(image_ref['pool']) as ioctx:
                 ioctx.set_namespace(image_ref['namespace'])
                 try: