From 50ec38470fa2fba460d06e341bea0943d5ff07db Mon Sep 17 00:00:00 2001 From: Prachi Goel Date: Sun, 11 Aug 2024 22:35:59 +0530 Subject: [PATCH] mgr/dashboard: rbd table actions enhancements MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- .../src/app/ceph/block/rbd-contants.ts | 14 ++++ .../block/rbd-form/rbd-form.component.html | 9 ++- .../ceph/block/rbd-form/rbd-form.component.ts | 13 ++-- .../block/rbd-list/rbd-list.component.spec.ts | 18 ++--- .../ceph/block/rbd-list/rbd-list.component.ts | 65 ++++++++++--------- .../help-text/help-text.component.scss | 2 +- .../table-actions/table-actions.component.ts | 4 +- .../src/app/shared/models/cd-table-action.ts | 2 + 8 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-contants.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 index 00000000000..c5b25191594 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-contants.ts @@ -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.` +}; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.html index 85c56cbf0d4..29a2008567e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.html @@ -7,7 +7,12 @@ novalidate>
{{ action | titlecase }} {{ resource | upperFirst }}
+ class="form-header">{{ action | titlecase }} {{ resource | upperFirst }} + +
{{copyMessage}} +
+
+
Mirroring can not be disabled on Pool mirror mode. + type="info">Mirroring can not be disabled on  Pool  mirror mode. You need to change the mirror mode to enable this option. = [ '4 KiB', '8 KiB', diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts index d71027bde3d..c775333a407 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts @@ -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: '', 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 1a4bb4e0cf8..52d9ff819e2 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 @@ -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; } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/help-text/help-text.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/help-text/help-text.component.scss index f7be01cd929..653ea5993a2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/help-text/help-text.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/help-text/help-text.component.scss @@ -1,3 +1,3 @@ -::ng-deep legend .text-muted { +.form-text { font-size: small; } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts index d8304127fab..51120f623f2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts @@ -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; } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts index e832665c5dc..f773422ac19 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts @@ -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 -- 2.39.5