]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: work with RBD images v1 35007/head
authorErnesto Puerta <epuertat@redhat.com>
Mon, 11 May 2020 18:33:25 +0000 (20:33 +0200)
committerErnesto Puerta <epuertat@redhat.com>
Thu, 4 Jun 2020 14:55:06 +0000 (16:55 +0200)
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 <epuertat@redhat.com>
qa/tasks/mgr/dashboard/test_rbd.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-model.ts
src/pybind/mgr/dashboard/services/ceph_service.py
src/pybind/mgr/dashboard/services/rbd.py
src/pybind/mgr/dashboard/tests/test_ceph_service.py

index 108b2f0332abfb30f91b2b90fad05b0729b6c520..2afad60ac039dbb938b9f913421de96b0595a8e2 100644 (file)
@@ -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),
index 6189e97ebf2201c8eaa021b88e05327cda1e4aa3..e702a8e6ff3d750ba40741b25782c0a26abca6ff 100644 (file)
                   class="bold">Order</td>
               <td>{{ selection.order }}</td>
             </tr>
+            <tr>
+              <td i18n
+                  class="bold">Format Version</td>
+              <td>{{ selection.image_format }}</td>
+            </tr>
           </tbody>
         </table>
       </ng-template>
index 112e2de300548a025310133315319bf47e49c7d0..95d728ed02abe37f9f5326c0f0ff0a01cc75a898 100644 (file)
@@ -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() {
index 098b297e1bbb663e9c2858f1c753b330613d7a79..dc198e90724f6d0b8d2329b463cb5ca3f7d931a4 100644 (file)
@@ -8,7 +8,7 @@
           [data]="images"
           columnMode="flex"
           [columns]="columns"
-          identifier="id"
+          identifier="unique_id"
           [searchableObjects]="true"
           forceIdentifier="true"
           selectionType="single"
index fc9b9478e3847daa94cdc040ff35d24cdd724f68..d8e8958d81bc9864016b88986a40be3188e4b2ff 100644 (file)
@@ -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,
index 625a2f251d06dbb713d57ce7b6c573264ef6102f..0a265dea8d14c242d7d9a6ec633e1e93c8fcffdf 100644 (file)
@@ -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
+}
index 5e4a5c15342d13bb5727c8a186616f5b3b250740..a6eee2957ecd2284b912206e811757af9eed7f3c 100644 (file)
@@ -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):
index 1734ca3bac7ac1b18569807a14da1c8337a15d00..4e628342282eb1ed5e92aa1ac3fe843f852c043f 100644 (file)
@@ -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()
index baf8588117bda129c670f10688bebcefa23fcb13..5111e68d492bd893e1710b21ae06323b698a7258 100644 (file)
@@ -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):