]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: rbd table actions enhancements
authorPrachi Goel <prachi@li-f0e8f2cc-27e0-11b2-a85c-b2772164128d.ibm.com>
Sun, 11 Aug 2024 17:05:59 +0000 (22:35 +0530)
committerPrachi Goel <prachi@li-f0e8f2cc-27e0-11b2-a85c-b2772164128d.ibm.com>
Fri, 27 Sep 2024 10:31:21 +0000 (16:01 +0530)
Fixes:https://tracker.ceph.com/issues/67198

Below are the changes:-
Delete option moved to last in list.
Delete is disabled in case image is secondary.
Move to trash should be second last option.
Tooltip is added for ‘Move to Trash’ and ‘Delete’
Tooltip is added for Copy option
Subheading is added for Copy Form.
Tooltip is added for ‘Flatten’ option.
Delete tooltip content has been in case image is primary.

Signed-off-by: Prachi Goel <prachi@li-f0e8f2cc-27e0-11b2-a85c-b2772164128d.ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-contants.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.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.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/help-text/help-text.component.scss
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts

diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-contants.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-contants.ts
new file mode 100644 (file)
index 0000000..c5b2519
--- /dev/null
@@ -0,0 +1,14 @@
+export const RBDActionHelpers = {
+  moveToTrash: $localize`Move an image to the trash. Images, even ones actively in-use by clones, can be moved to the trash and deleted at a later time.`,
+  delete: $localize`Delete an rbd image (including all data blocks). If the image has snapshots, this fails and nothing is deleted.`,
+  copy: $localize`Copy the content of a source image into the newly created destination image`,
+  flatten: $localize`If the image is a clone, copy all shared blocks from the parent snapshot and make the child independent of the parent, severing the link between parent snap and child. `,
+  enableMirroring: $localize`Mirroring needs to be enabled on the image to perform this action`,
+  clonedSnapshot: $localize`This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD`,
+  secondayImageDelete: $localize`The image cannot be deleted as it is secondary`,
+  primaryImageResync: $localize`Primary RBD images cannot be resynced`,
+  invalidNameDisable: $localize`This RBD image has an invalid name and can't be managed by ceph.`,
+  removingStatus: $localize`Action not possible for an RBD in status 'Removing'`,
+  journalTooltipText: $localize`'Ensures reliable replication by logging changes before updating the image, but doubles write time, impacting performance. Not recommended for high-speed data processing tasks.`,
+  snapshotTooltipText: $localize`This mode replicates RBD images between clusters using snapshots, efficiently copying data changes but requiring complete delta syncing during failover. Ideal for less demanding tasks due to its less granular approach compared to journaling.`
+};
index 85c56cbf0d454e31c8dd10accbf3e5cb1edf9f33..29a2008567e4c80897caaeed1981e94e3203f3a7 100644 (file)
@@ -7,7 +7,12 @@
           novalidate>
 
       <div i18n="form title"
-           class="form-header">{{ action | titlecase }} {{ resource | upperFirst }}</div>
+           class="form-header">{{ action | titlecase }} {{ resource | upperFirst }}
+        <cd-help-text>
+          <div *ngIf="action === 'Copy'">{{copyMessage}}
+          </div>
+        </cd-help-text>
+      </div>
 
       <!-- Parent -->
       <div class="form-item"
         <cd-alert-panel *ngIf="showMirrorDisableMessage"
                         spacingClass="mt-2"
                         [showTitle]="false"
-                        type="info">Mirroring can not be disabled on <b>Pool</b> mirror mode.
+                        type="info">Mirroring can not be disabled on <b>&nbsp;Pool&nbsp;</b> mirror mode.
                                     You need to change the mirror mode to enable this option.
         </cd-alert-panel>
         <cd-alert-panel *ngIf="currentPoolMirrorMode === 'disabled'"
index d9c1c8925fce45531298ac369f9914177b5201a6..7d694e2cab4a250071f130b0ccea53ebefd1f745 100644 (file)
@@ -34,6 +34,7 @@ import { RbdFormEditRequestModel } from './rbd-form-edit-request.model';
 import { RbdFormMode } from './rbd-form-mode.enum';
 import { RbdFormResponseModel } from './rbd-form-response.model';
 import { CdValidators } from '~/app/shared/forms/cd-validators';
+import { RBDActionHelpers } from '../rbd-contants';
 
 class ExternalData {
   rbd: RbdFormResponseModel;
@@ -69,34 +70,28 @@ export class RbdFormComponent extends CdForm implements OnInit {
 
   pool: string;
   peerConfigured = false;
-
   advancedEnabled = false;
-
   public rbdFormMode = RbdFormMode;
   mode: RbdFormMode;
-
   response: RbdFormResponseModel;
   snapName: string;
-
   defaultObjectSize = '4 MiB';
 
   mirroringOptions = [
     {
       value: 'journal',
-      text:
-        'Ensures reliable replication by logging changes before updating the image, but doubles write time, impacting performance. Not recommended for high-speed data processing tasks.'
+      text: RBDActionHelpers.journalTooltipText
     },
     {
       value: 'snapshot',
-      text:
-        'This mode replicates RBD images between clusters using snapshots, efficiently copying data changes but requiring complete delta syncing during failover. Ideal for less demanding tasks due to its less granular approach compared to journaling.'
+      text: RBDActionHelpers.snapshotTooltipText
     }
   ];
   poolMirrorMode: string;
   mirroring = false;
   currentPoolName = '';
   currentPoolMirrorMode = '';
-
+  copyMessage: string = RBDActionHelpers.copy;
   objectSizes: Array<string> = [
     '4 KiB',
     '8 KiB',
index d71027bde3dd91807d336f66ec42c5fb86f25ef7..c775333a40748211683dd22c448e12a22bc9e5b2 100644 (file)
@@ -128,7 +128,7 @@ describe('RbdListComponent', () => {
         ]
       });
       expect(component.getDeleteDisableDesc(component.selection)).toBe(
-        'This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.'
+        'This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD'
       );
     });
 
@@ -268,11 +268,11 @@ describe('RbdListComponent', () => {
           'Copy',
           'Flatten',
           'Resync',
-          'Delete',
-          'Move to Trash',
           'Remove Scheduling',
           'Promote',
-          'Demote'
+          'Demote',
+          'Move to Trash',
+          'Delete'
         ],
         primary: {
           multiple: 'Create',
@@ -300,7 +300,7 @@ describe('RbdListComponent', () => {
         }
       },
       'create,delete': {
-        actions: ['Create', 'Copy', 'Delete', 'Move to Trash'],
+        actions: ['Create', 'Copy', 'Move to Trash', 'Delete'],
         primary: {
           multiple: 'Create',
           executing: 'Create',
@@ -322,11 +322,11 @@ describe('RbdListComponent', () => {
           'Edit',
           'Flatten',
           'Resync',
-          'Delete',
-          'Move to Trash',
           'Remove Scheduling',
           'Promote',
-          'Demote'
+          'Demote',
+          'Move to Trash',
+          'Delete'
         ],
         primary: {
           multiple: '',
@@ -345,7 +345,7 @@ describe('RbdListComponent', () => {
         }
       },
       delete: {
-        actions: ['Delete', 'Move to Trash'],
+        actions: ['Move to Trash', 'Delete'],
         primary: {
           multiple: '',
           executing: '',
index 1a4bb4e0cf86a42b78a9a83daf646b1b097b1713..52d9ff819e2fb605c4946f1e31d6f80d492d4e57 100644 (file)
@@ -23,7 +23,6 @@ import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
 import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { CdTableServerSideService } from '~/app/shared/services/cd-table-server-side.service';
-// import { ModalService } from '~/app/shared/services/modal.service';
 import { TaskListService } from '~/app/shared/services/task-list.service';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { URLBuilderService } from '~/app/shared/services/url-builder.service';
@@ -32,7 +31,7 @@ import { RbdParentModel } from '../rbd-form/rbd-parent.model';
 import { RbdTrashMoveModalComponent } from '../rbd-trash-move-modal/rbd-trash-move-modal.component';
 import { RBDImageFormat, RbdModel } from './rbd-model';
 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
-
+import { RBDActionHelpers } from '../rbd-contants';
 const BASE_URL = 'block/rbd';
 
 @Component({
@@ -83,7 +82,6 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
   count = 0;
   private tableContext: CdTableFetchDataContext = null;
   errorMessage: string;
-
   builders = {
     'rbd/create': (metadata: object) =>
       this.createRbdFromTask(metadata['pool_name'], metadata['namespace'], metadata['image_name']),
@@ -159,8 +157,20 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
       icon: Icons.destroy,
       click: () => this.deleteRbdModal(),
       name: this.actionLabels.DELETE,
+      title: RBDActionHelpers.delete,
       disable: (selection: CdTableSelection) => this.getDeleteDisableDesc(selection)
     };
+    const moveAction: CdTableAction = {
+      permission: 'delete',
+      icon: Icons.trash,
+      title: RBDActionHelpers.moveToTrash,
+      click: () => this.trashRbdModal(),
+      name: this.actionLabels.TRASH,
+      disable: (selection: CdTableSelection) =>
+        this.getRemovingStatusDesc(selection) ||
+        this.getInvalidNameDisable(selection) ||
+        selection.first().image_format === RBDImageFormat.V1
+    };
     const resyncAction: CdTableAction = {
       permission: 'update',
       icon: Icons.refresh,
@@ -177,7 +187,8 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
         !!selection.first().cdExecuting,
       icon: Icons.copy,
       routerLink: () => `/block/rbd/copy/${getImageUri()}`,
-      name: this.actionLabels.COPY
+      name: this.actionLabels.COPY,
+      title: RBDActionHelpers.copy
     };
     const flattenAction: CdTableAction = {
       permission: 'update',
@@ -188,18 +199,10 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
         !selection.first().parent,
       icon: Icons.flatten,
       click: () => this.flattenRbdModal(),
-      name: this.actionLabels.FLATTEN
-    };
-    const moveAction: CdTableAction = {
-      permission: 'delete',
-      icon: Icons.trash,
-      click: () => this.trashRbdModal(),
-      name: this.actionLabels.TRASH,
-      disable: (selection: CdTableSelection) =>
-        this.getRemovingStatusDesc(selection) ||
-        this.getInvalidNameDisable(selection) ||
-        selection.first().image_format === RBDImageFormat.V1
+      name: this.actionLabels.FLATTEN,
+      title: RBDActionHelpers.flatten
     };
+
     const removeSchedulingAction: CdTableAction = {
       permission: 'update',
       icon: Icons.edit,
@@ -217,9 +220,7 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
       name: this.actionLabels.PROMOTE,
       visible: () => this.selection.first() != null && !this.selection.first().primary,
       disable: () =>
-        this.selection.first().mirror_mode === 'Disabled'
-          ? 'Mirroring needs to be enabled on the image to perform this action'
-          : ''
+        this.selection.first().mirror_mode === 'Disabled' ? RBDActionHelpers.enableMirroring : ''
     };
     const demoteAction: CdTableAction = {
       permission: 'update',
@@ -228,9 +229,7 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
       name: this.actionLabels.DEMOTE,
       visible: () => this.selection.first() != null && this.selection.first().primary,
       disable: () =>
-        this.selection.first().mirror_mode === 'Disabled'
-          ? 'Mirroring needs to be enabled on the image to perform this action'
-          : ''
+        this.selection.first().mirror_mode === 'Disabled' ? RBDActionHelpers.enableMirroring : ''
     };
     this.tableActions = [
       addAction,
@@ -238,11 +237,11 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
       copyAction,
       flattenAction,
       resyncAction,
-      deleteAction,
-      moveAction,
       removeSchedulingAction,
       promoteAction,
-      demoteAction
+      demoteAction,
+      moveAction,
+      deleteAction
     ];
   }
 
@@ -624,17 +623,23 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
     const first = selection.first();
 
     if (first && this.hasClonedSnapshots(first)) {
-      return $localize`This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.`;
+      return RBDActionHelpers.clonedSnapshot;
     }
-
-    return this.getInvalidNameDisable(selection) || this.hasClonedSnapshots(selection.first());
+    if (first && first.primary === false) {
+      return RBDActionHelpers.secondayImageDelete;
+    }
+    return (
+      this.getInvalidNameDisable(selection) ||
+      this.hasClonedSnapshots(selection.first()) ||
+      first.primary === false
+    );
   }
 
   getResyncDisableDesc(selection: CdTableSelection): string | boolean {
     const first = selection.first();
 
     if (first && this.imageIsPrimary(first)) {
-      return $localize`Primary RBD images cannot be resynced`;
+      return RBDActionHelpers.primaryImageResync;
     }
 
     return this.getInvalidNameDisable(selection);
@@ -647,7 +652,7 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
     const first = selection.first();
 
     if (first?.name?.match(/[@/]/)) {
-      return $localize`This RBD image has an invalid name and can't be managed by ceph.`;
+      return RBDActionHelpers.invalidNameDisable;
     }
 
     return !selection.first() || !selection.hasSingleSelection;
@@ -656,7 +661,7 @@ export class RbdListComponent extends ListWithDetails implements OnInit {
   getRemovingStatusDesc(selection: CdTableSelection): string | boolean {
     const first = selection.first();
     if (first?.source === 'REMOVING') {
-      return $localize`Action not possible for an RBD in status 'Removing'`;
+      return RBDActionHelpers.removingStatus;
     }
     return false;
   }
index d8304127fab13eb5bc737b7223c4f4b567193594..51120f623f20980964c5ee6d68cf99218dd778f4 100644 (file)
@@ -147,7 +147,9 @@ export class TableActionsComponent implements OnChanges, OnInit {
   useDisableDesc(action: CdTableAction) {
     if (action.disable) {
       const result = action.disable(this.selection);
-      return _.isString(result) ? result : undefined;
+      return _.isString(result) ? result : action.title ? action.title : undefined;
+    } else if (action.title) {
+      return action.title;
     }
     return undefined;
   }
index e832665c5dc096334baa3f177b73595df8ac73cc..f773422ac195f5513391c8d5964f85302ad5fb45 100644 (file)
@@ -19,6 +19,8 @@ export class CdTableAction {
   // The font awesome icon that will be used
   icon: string;
 
+  // For adding the default tooltip
+  title?: string;
   /**
    * You can define the condition to disable the action.
    * By default all 'update' and 'delete' actions will only be enabled