From: Pere Diaz Bou Date: Fri, 15 Jul 2022 14:30:15 +0000 (+0200) Subject: mgr/dashboard: fix _get_rbd_image_refs caching X-Git-Tag: v18.0.0~254^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=950e66b1207f50e3bc91382e07540181d5059511;p=ceph.git mgr/dashboard: fix _get_rbd_image_refs caching We were cahing _get_rbd_image_refs by Ioctx but the namespace to which a ioctx is set is also relevant information regarding the references of a pool. Signed-off-by: Pere Diaz Bou --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html index 004ff22c3a4e2..712d771c5d4e0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html @@ -12,6 +12,7 @@ selectionType="single" [hasDetails]="true" [status]="tableStatus" + [maxLimit]="25" [autoReload]="-1" (fetchData)="taskListService.fetch($event)" (setExpandedRow)="setExpandedRow($event)" diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html index 2d2a8b5e95a5c..d7dd7e5c703ff 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html @@ -205,12 +205,21 @@ {{ selectedCount }} selected / - - {{ rowCount }} found / - - + + + + + {{ rowCount }} found / + {{ data?.length || 0 }} total + + + {{ data?.length || 0 }} found / + {{ rowCount }} total + 0) { - this.userConfig.limit = value; + if (this.maxLimit && value > this.maxLimit) { + this.userConfig.limit = this.maxLimit; + // change input field to maxLimit + e.srcElement.value = this.maxLimit; + } else { + this.userConfig.limit = value; + } } if (this.serverSide) { this.reloadData(); diff --git a/src/pybind/mgr/dashboard/services/rbd.py b/src/pybind/mgr/dashboard/services/rbd.py index 5a71d7db40bbb..d2a0312029b34 100644 --- a/src/pybind/mgr/dashboard/services/rbd.py +++ b/src/pybind/mgr/dashboard/services/rbd.py @@ -241,6 +241,7 @@ class RbdConfiguration(object): class RbdService(object): + _rbd_inst = rbd.RBD() @classmethod def _rbd_disk_usage(cls, image, snaps, whole_object=True): @@ -390,9 +391,32 @@ class RbdService(object): @classmethod @ttl_cache(10) - def _rbd_image_refs(cls, ioctx): - rbd_inst = rbd.RBD() - return rbd_inst.list2(ioctx) + def get_ioctx(cls, pool_name, namespace=''): + ioctx = mgr.rados.open_ioctx(pool_name) + ioctx.set_namespace(namespace) + return ioctx + + @classmethod + @ttl_cache(30) + def _rbd_image_refs(cls, pool_name, namespace=''): + # We add and set the namespace here so that we cache by ioctx and namespace. + images = [] + ioctx = cls.get_ioctx(pool_name, namespace) + images = cls._rbd_inst.list2(ioctx) + return images + + @classmethod + @ttl_cache(30) + def _pool_namespaces(cls, pool_name, namespace=None): + namespaces = [] + if namespace: + namespaces = [namespace] + else: + ioctx = cls.get_ioctx(pool_name, namespace=rados.LIBRADOS_ALL_NSPACES) + namespaces = cls._rbd_inst.namespace_list(ioctx) + # images without namespace + namespaces.append('') + return namespaces @classmethod def _rbd_image_stat(cls, ioctx, pool_name, namespace, image_name): @@ -400,8 +424,7 @@ class RbdService(object): @classmethod def _rbd_image_stat_removing(cls, ioctx, pool_name, namespace, image_id): - rbd_inst = rbd.RBD() - img = rbd_inst.trash_get(ioctx, image_id) + img = cls._rbd_inst.trash_get(ioctx, image_id) img_spec = get_image_spec(pool_name, namespace, image_id) if img['source'] == 'REMOVING': @@ -417,22 +440,13 @@ class RbdService(object): @classmethod def _rbd_pool_image_refs(cls, pool_names: List[str], namespace: Optional[str] = None): joint_refs = [] - rbd_inst = rbd.RBD() for pool in pool_names: - with mgr.rados.open_ioctx(pool) as ioctx: - if namespace: - namespaces = [namespace] - else: - namespaces = rbd_inst.namespace_list(ioctx) - # images without namespace - namespaces.append('') - for current_namespace in namespaces: - ioctx.set_namespace(current_namespace) - image_refs = cls._rbd_image_refs(ioctx) - for image in image_refs: - image['namespace'] = current_namespace - image['pool_name'] = pool - joint_refs.append(image) + for current_namespace in cls._pool_namespaces(pool, namespace=namespace): + image_refs = cls._rbd_image_refs(pool, current_namespace) + for image in image_refs: + image['namespace'] = current_namespace + image['pool_name'] = pool + joint_refs.append(image) return joint_refs @classmethod @@ -468,20 +482,19 @@ class RbdService(object): end = len(image_refs) 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_name']) as ioctx: - ioctx.set_namespace(image_ref['namespace']) + ioctx = cls.get_ioctx(image_ref['pool_name'], namespace=image_ref['namespace']) + try: + stat = cls._rbd_image_stat( + ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['name']) + except rbd.ImageNotFound: + # Check if the RBD has been deleted partially. This happens for example if + # the deletion process of the RBD has been started and was interrupted. try: - stat = cls._rbd_image_stat( - ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['name']) + stat = cls._rbd_image_stat_removing( + ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['id']) except rbd.ImageNotFound: - # Check if the RBD has been deleted partially. This happens for example if - # the deletion process of the RBD has been started and was interrupted. - try: - stat = cls._rbd_image_stat_removing( - ioctx, image_ref['pool_name'], image_ref['namespace'], image_ref['id']) - except rbd.ImageNotFound: - continue - result.append(stat) + continue + result.append(stat) return result, len(image_refs) @classmethod diff --git a/src/pybind/mgr/dashboard/tests/test_rbd_service.py b/src/pybind/mgr/dashboard/tests/test_rbd_service.py index 3e82ceda62428..afd316c41cbbc 100644 --- a/src/pybind/mgr/dashboard/tests/test_rbd_service.py +++ b/src/pybind/mgr/dashboard/tests/test_rbd_service.py @@ -23,6 +23,11 @@ class ImageNotFoundStub(Exception): class RbdServiceTest(unittest.TestCase): + def setUp(self): + # pylint: disable=protected-access + RbdService._rbd_inst = mock.Mock() + self.rbd_inst_mock = RbdService._rbd_inst + def test_compose_image_spec(self): self.assertEqual(get_image_spec('mypool', 'myns', 'myimage'), 'mypool/myns/myimage') self.assertEqual(get_image_spec('mypool', None, 'myimage'), 'mypool/myimage') @@ -54,11 +59,9 @@ class RbdServiceTest(unittest.TestCase): config = RbdConfiguration('good-pool') self.assertEqual(config.list(), [1, 2, 3]) - @mock.patch('dashboard.services.rbd.rbd.RBD') - def test_rbd_image_stat_removing(self, rbd_mock): + def test_rbd_image_stat_removing(self): time = datetime.utcnow() - rbd_inst_mock = rbd_mock.return_value - rbd_inst_mock.trash_get.return_value = { + self.rbd_inst_mock.trash_get.return_value = { 'id': '3c1a5ee60a88', 'name': 'test_rbd', 'source': 'REMOVING', @@ -82,10 +85,8 @@ class RbdServiceTest(unittest.TestCase): }) @mock.patch('dashboard.services.rbd.rbd.ImageNotFound', new_callable=lambda: ImageNotFoundStub) - @mock.patch('dashboard.services.rbd.rbd.RBD') - def test_rbd_image_stat_filter_source_user(self, rbd_mock, _): - rbd_inst_mock = rbd_mock.return_value - rbd_inst_mock.trash_get.return_value = { + def test_rbd_image_stat_filter_source_user(self, _): + self.rbd_inst_mock.trash_get.return_value = { 'id': '3c1a5ee60a88', 'name': 'test_rbd', 'source': 'USER' @@ -99,21 +100,21 @@ class RbdServiceTest(unittest.TestCase): str(ctx.exception)) @mock.patch('dashboard.services.rbd.rbd.ImageNotFound', new_callable=lambda: ImageNotFoundStub) + @mock.patch('dashboard.services.rbd.RbdService._pool_namespaces') @mock.patch('dashboard.services.rbd.RbdService._rbd_image_stat_removing') @mock.patch('dashboard.services.rbd.RbdService._rbd_image_stat') @mock.patch('dashboard.services.rbd.RbdService._rbd_image_refs') - @mock.patch('dashboard.services.rbd.rbd.RBD') - def test_rbd_pool_list(self, rbd_mock, rbd_image_ref_mock, rbd_image_stat_mock, - rbd_image_stat_removing_mock, _): + def test_rbd_pool_list(self, rbd_image_ref_mock, rbd_image_stat_mock, + rbd_image_stat_removing_mock, pool_namespaces, _): time = datetime.utcnow() ioctx_mock = MagicMock() mgr.rados = MagicMock() mgr.rados.open_ioctx.return_value = ioctx_mock - rbd_inst_mock = rbd_mock.return_value - rbd_inst_mock.namespace_list.return_value = [] + self.rbd_inst_mock.namespace_list.return_value = [] rbd_image_ref_mock.return_value = [{'name': 'test_rbd', 'id': '3c1a5ee60a88'}] + pool_namespaces.return_value = [''] rbd_image_stat_mock.side_effect = mock.Mock(side_effect=ImageNotFoundStub( 'RBD image not found test_pool/3c1a5ee60a88')) @@ -158,3 +159,15 @@ class RbdServiceTest(unittest.TestCase): RBDSchedulerInterval(interval) else: self.assertEqual(str(RBDSchedulerInterval(interval)), interval) + + def test_rbd_image_refs_cache(self): + ioctx_mock = MagicMock() + mgr.rados = MagicMock() + mgr.rados.open_ioctx.return_value = ioctx_mock + images = [{'image': str(i), 'id': str(i)} for i in range(10)] + for i in range(5): + self.rbd_inst_mock.list2.return_value = images[i*2:(i*2)+2] + ioctx_mock = MagicMock() + # pylint: disable=protected-access + res = RbdService._rbd_image_refs(ioctx_mock, str(i)) + self.assertEqual(res, images[i*2:(i*2)+2])