]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: work with RBD images v1 35712/head
authorErnesto Puerta <epuertat@redhat.com>
Mon, 11 May 2020 18:33:25 +0000 (20:33 +0200)
committerErnesto Puerta <epuertat@redhat.com>
Fri, 10 Jul 2020 11:46:02 +0000 (13:46 +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>
(cherry picked from commit 119851ec89745026c273334efbcbf2d50128c99d)

 Conflicts:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html: add row
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts: add imports
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-model.ts: add new fields
src/pybind/mgr/dashboard/services/ceph_service.py: just add get_pool_name_from_id
src/pybind/mgr/dashboard/services/rbd.py: discard changes
src/pybind/mgr/dashboard/controllers/rbd.py: bring and adapt code from services/rbd.py

Signed-off-by: Ernesto Puerta <epuertat@redhat.com>
qa/tasks/mgr/dashboard/test_rbd.py
src/pybind/mgr/dashboard/controllers/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/tests/test_ceph_service.py

index 252ca82df32abab79754b6af9bd3df868c72226e..68af93d9b9cefd338ff96d006ac36868d5f89c5b 100644 (file)
@@ -188,6 +188,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),
             'features': JLeaf(int),
             'features_name': JList(JLeaf(str)),
index 8555a35ea12542d807a76815d6a725709f76b2b6..8e0b184c9b7cc1331f8d6f8c3014910538e90a0c 100644 (file)
@@ -105,7 +105,15 @@ class Rbd(RESTController):
         with rbd.Image(ioctx, image_name) as img:
             stat = img.stat()
             stat['name'] = image_name
-            stat['id'] = img.id()
+            if img.old_format():
+                stat['unique_id'] = '{}/{}'.format(pool_name, stat['block_name_prefix'])
+                stat['id'] = stat['unique_id']
+                stat['image_format'] = 1
+            else:
+                stat['unique_id'] = '{}/{}'.format(pool_name, img.id())
+                stat['id'] = img.id()
+                stat['image_format'] = 2
+
             stat['pool_name'] = pool_name
             features = img.features()
             stat['features'] = features
index 889cab1f9a4bda293e97751b11310c473b000410..2c24eb75ae00010287d49848c6cf5a972e79622a 100644 (file)
               class="bold col-sm-1">Order</td>
           <td class="col-sm-3">{{ selectedItem.order }}</td>
         </tr>
+        <tr>
+          <td i18n
+              class="bold">Format Version</td>
+          <td>{{ selection.image_format }}</td>
+        </tr>
       </tbody>
     </table>
   </tab>
index 6e9bb294ce1d4ee6bc301134f38d8aa572047b9a..ecd158cb5afe18e13a688a66cabbd8b1f74fb5b5 100644 (file)
@@ -21,6 +21,7 @@ import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
 import { FormatterService } from '../../../shared/services/formatter.service';
 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
+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';
@@ -188,6 +189,15 @@ export class RbdFormComponent 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 ccd95925353571c5afdbcada2ef6ad2bd956c43a..4dd424e23df81451dc47a45ff65a0279c39827ea 100644 (file)
@@ -6,7 +6,7 @@
           [data]="images"
           columnMode="flex"
           [columns]="columns"
-          identifier="id"
+          identifier="unique_id"
           [searchableObjects]="true"
           forceIdentifier="true"
           selectionType="single"
index 2ddaa5ce33bc9726e5da2e7d0de59255d79b3140..596941912cd80cf8f36333b7a1cf296ecdba4071 100644 (file)
@@ -24,7 +24,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';
 
@@ -71,8 +71,10 @@ export class RbdListComponent implements OnInit {
   private createRbdFromTask(pool: string, name: string): RbdModel {
     const model = new RbdModel();
     model.id = '-1';
+    model.unique_id = '-1';
     model.name = name;
     model.pool_name = pool;
+    model.image_format = RBDImageFormat.V2;
     return model;
   }
 
@@ -134,7 +136,11 @@ export class RbdListComponent implements OnInit {
       permission: 'delete',
       icon: 'fa-trash-o',
       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 92a77bdd6eaa4cf3e50d9dca726120cd2505c3bb..3920b6ff21a3676a237422e6255f79498c1b3f6e 100644 (file)
@@ -1,7 +1,14 @@
 export class RbdModel {
   id: string;
+  unique_id: string;
   name: string;
   pool_name: string;
+  image_format: RBDImageFormat;
 
   cdExecuting: string;
 }
+
+export enum RBDImageFormat {
+  V1 = 1,
+  V2 = 2
+}
index 9b50c093a8e55234b768bfcff49842650f92c7ae..6d38fa410798793ae5e39edbc526f1d8ec244f16 100644 (file)
@@ -118,8 +118,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 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):