From 119851ec89745026c273334efbcbf2d50128c99d Mon Sep 17 00:00:00 2001 From: Ernesto Puerta Date: Mon, 11 May 2020 20:33:25 +0200 Subject: [PATCH] mgr/dashboard: work with RBD images v1 Add support for RBD Image Format v1: - This format lacks ID field, required for dashboard. Instead, RBD image `block_name_prefix` is used as unique ID (together with pool id and namespace) - Additionally, `image_format` is now exposed. - In the front-end side: - Copy action on a v1 image will cause the image to be copied to v2 format. - List doesn't allow Move to Trash on v1 images, - Details section now shows `image_format` for images, - Edit Form disables flags not supported for v1 (`deep-flatten`, `layering`, `exclusive-lock`). - Protect does not work on v1 images or v2 images created from v1 ones. Fixes: https://tracker.ceph.com/issues/36354 Signed-off-by: Ernesto Puerta --- qa/tasks/mgr/dashboard/test_rbd.py | 2 ++ .../ceph/block/rbd-details/rbd-details.component.html | 5 +++++ .../src/app/ceph/block/rbd-form/rbd-form.component.ts | 10 ++++++++++ .../app/ceph/block/rbd-list/rbd-list.component.html | 2 +- .../src/app/ceph/block/rbd-list/rbd-list.component.ts | 10 ++++++++-- .../frontend/src/app/ceph/block/rbd-list/rbd-model.ts | 7 +++++++ src/pybind/mgr/dashboard/services/ceph_service.py | 3 +-- src/pybind/mgr/dashboard/services/rbd.py | 10 +++++++++- src/pybind/mgr/dashboard/tests/test_ceph_service.py | 6 ++++-- 9 files changed, 47 insertions(+), 8 deletions(-) diff --git a/qa/tasks/mgr/dashboard/test_rbd.py b/qa/tasks/mgr/dashboard/test_rbd.py index 108b2f0332abf..2afad60ac039d 100644 --- a/qa/tasks/mgr/dashboard/test_rbd.py +++ b/qa/tasks/mgr/dashboard/test_rbd.py @@ -236,6 +236,8 @@ class RbdTest(DashboardTestCase): 'block_name_prefix': JLeaf(str), 'name': JLeaf(str), 'id': JLeaf(str), + 'unique_id': JLeaf(str), + 'image_format': JLeaf(int), 'pool_name': JLeaf(str), 'namespace': JLeaf(str, none=True), 'features': JLeaf(int), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html index 6189e97ebf220..e702a8e6ff3d7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html @@ -115,6 +115,11 @@ class="bold">Order {{ selection.order }} + + Format Version + {{ selection.image_format }} + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts index 112e2de300548..95d728ed02abe 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts @@ -26,6 +26,7 @@ import { AuthStorageService } from '../../../shared/services/auth-storage.servic import { FormatterService } from '../../../shared/services/formatter.service'; import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; import { Pool } from '../../pool/pool'; +import { RBDImageFormat, RbdModel } from '../rbd-list/rbd-model'; import { RbdImageFeature } from './rbd-feature.interface'; import { RbdFormCloneRequestModel } from './rbd-form-clone-request.model'; import { RbdFormCopyRequestModel } from './rbd-form-copy-request.model'; @@ -200,6 +201,15 @@ export class RbdFormComponent extends CdForm implements OnInit { this.rbdForm.get('obj_size').disable(); this.rbdForm.get('stripingUnit').disable(); this.rbdForm.get('stripingCount').disable(); + + /* RBD Image Format v1 */ + this.rbdImage.subscribe((image: RbdModel) => { + if (image.image_format === RBDImageFormat.V1) { + this.rbdForm.get('deep-flatten').disable(); + this.rbdForm.get('layering').disable(); + this.rbdForm.get('exclusive-lock').disable(); + } + }); } disableForClone() { 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 098b297e1bbb6..dc198e90724f6 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 @@ -8,7 +8,7 @@ [data]="images" columnMode="flex" [columns]="columns" - identifier="id" + identifier="unique_id" [searchableObjects]="true" forceIdentifier="true" selectionType="single" diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts index fc9b9478e3847..d8e8958d81bc9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts @@ -28,7 +28,7 @@ import { TaskWrapperService } from '../../../shared/services/task-wrapper.servic import { URLBuilderService } from '../../../shared/services/url-builder.service'; import { RbdParentModel } from '../rbd-form/rbd-parent.model'; import { RbdTrashMoveModalComponent } from '../rbd-trash-move-modal/rbd-trash-move-modal.component'; -import { RbdModel } from './rbd-model'; +import { RBDImageFormat, RbdModel } from './rbd-model'; const BASE_URL = 'block/rbd'; @@ -91,9 +91,11 @@ export class RbdListComponent extends ListWithDetails implements OnInit { private createRbdFromTask(pool: string, namespace: string, name: string): RbdModel { const model = new RbdModel(); model.id = '-1'; + model.unique_id = '-1'; model.name = name; model.namespace = namespace; model.pool_name = pool; + model.image_format = RBDImageFormat.V2; return model; } @@ -163,7 +165,11 @@ export class RbdListComponent extends ListWithDetails implements OnInit { permission: 'delete', icon: Icons.trash, click: () => this.trashRbdModal(), - name: this.actionLabels.TRASH + name: this.actionLabels.TRASH, + disable: (selection: CdTableSelection) => + !selection.first() || + !selection.hasSingleSelection || + selection.first().image_format === RBDImageFormat.V1 }; this.tableActions = [ addAction, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-model.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-model.ts index 625a2f251d06d..0a265dea8d14c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-model.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-model.ts @@ -1,8 +1,15 @@ export class RbdModel { id: string; + unique_id: string; name: string; pool_name: string; namespace: string; + image_format: RBDImageFormat; cdExecuting: string; } + +export enum RBDImageFormat { + V1 = 1, + V2 = 2 +} diff --git a/src/pybind/mgr/dashboard/services/ceph_service.py b/src/pybind/mgr/dashboard/services/ceph_service.py index 5e4a5c15342d1..a6eee2957ecd2 100644 --- a/src/pybind/mgr/dashboard/services/ceph_service.py +++ b/src/pybind/mgr/dashboard/services/ceph_service.py @@ -139,8 +139,7 @@ class CephService(object): @classmethod def get_pool_name_from_id(cls, pool_id): # type: (int) -> Union[str, None] - pool = cls.get_pool_by_attribute('pool', pool_id) - return pool['pool_name'] if pool is not None else None + return mgr.rados.pool_reverse_lookup(pool_id) @classmethod def get_pool_by_attribute(cls, attribute, value): diff --git a/src/pybind/mgr/dashboard/services/rbd.py b/src/pybind/mgr/dashboard/services/rbd.py index 1734ca3bac7ac..4e628342282eb 100644 --- a/src/pybind/mgr/dashboard/services/rbd.py +++ b/src/pybind/mgr/dashboard/services/rbd.py @@ -253,7 +253,15 @@ class RbdService(object): stat = img.stat() stat['name'] = image_name - stat['id'] = img.id() + if img.old_format(): + stat['unique_id'] = get_image_spec(pool_name, namespace, stat['block_name_prefix']) + stat['id'] = stat['unique_id'] + stat['image_format'] = 1 + else: + stat['unique_id'] = get_image_spec(pool_name, namespace, img.id()) + stat['id'] = img.id() + stat['image_format'] = 2 + stat['pool_name'] = pool_name stat['namespace'] = namespace features = img.features() diff --git a/src/pybind/mgr/dashboard/tests/test_ceph_service.py b/src/pybind/mgr/dashboard/tests/test_ceph_service.py index baf8588117bda..5111e68d492bd 100644 --- a/src/pybind/mgr/dashboard/tests/test_ceph_service.py +++ b/src/pybind/mgr/dashboard/tests/test_ceph_service.py @@ -52,10 +52,12 @@ class CephServiceTest(unittest.TestCase): def test_get_pool_by_attribute_matching_a_not_always_set_attribute(self): self.assertEqual(self.service.get_pool_by_attribute('flaky', 'option_x'), self.pools[1]) - def test_get_pool_name_from_id_with_match(self): + @mock.patch('dashboard.mgr.rados.pool_reverse_lookup', return_value='good_pool') + def test_get_pool_name_from_id_with_match(self, _mock): self.assertEqual(self.service.get_pool_name_from_id(1), 'good_pool') - def test_get_pool_name_from_id_without_match(self): + @mock.patch('dashboard.mgr.rados.pool_reverse_lookup', return_value=None) + def test_get_pool_name_from_id_without_match(self, _mock): self.assertEqual(self.service.get_pool_name_from_id(3), None) def test_get_pool_pg_status(self): -- 2.39.5