]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: added delete and activation actions 55494/head
authorIvo Almeida <ialmeida@redhat.com>
Wed, 7 Feb 2024 14:03:45 +0000 (14:03 +0000)
committerIvo Almeida <ialmeida@redhat.com>
Sun, 11 Feb 2024 20:00:24 +0000 (20:00 +0000)
Fixes: https://tracker.ceph.com/issues/64355
Signed-off-by: Ivo Almeida <ialmeida@redhat.com>
src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-snapshot-schedule.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts
src/pybind/mgr/dashboard/openapi.yaml

index 1437962723ff2aa795894544ae5771a4e4131695..83e9cbfd8533faf8c1a12a2e3848a71137156d65 100644 (file)
@@ -1025,3 +1025,54 @@ class CephFSSnapshotSchedule(RESTController):
         editRetentionPolicies('snap_schedule_retention_add', retention_to_add)
 
         return f'Retention policies for snapshot schedule on path {path} updated successfully'
+
+    @RESTController.Resource('DELETE')
+    def delete_snapshot(self, fs: str, path: str, schedule: str, start: str):
+        error_code, _, err = mgr.remote('snap_schedule',
+                                        'snap_schedule_rm',
+                                        path,
+                                        schedule,
+                                        start,
+                                        fs,
+                                        None,
+                                        None)
+        if error_code != 0:
+            raise DashboardException(
+                f'Failed to delete snapshot schedule for path {path}: {err}'
+            )
+
+        return f'Snapshot schedule for path {path} deleted successfully'
+
+    @RESTController.Resource('POST')
+    def deactivate(self, fs: str, path: str, schedule: str, start: str):
+        error_code, _, err = mgr.remote('snap_schedule',
+                                        'snap_schedule_deactivate',
+                                        path,
+                                        schedule,
+                                        start,
+                                        fs,
+                                        None,
+                                        None)
+        if error_code != 0:
+            raise DashboardException(
+                f'Failed to deactivate snapshot schedule for path {path}: {err}'
+            )
+
+        return f'Snapshot schedule for path {path} deactivated successfully'
+
+    @RESTController.Resource('POST')
+    def activate(self, fs: str, path: str, schedule: str, start: str):
+        error_code, _, err = mgr.remote('snap_schedule',
+                                        'snap_schedule_activate',
+                                        path,
+                                        schedule,
+                                        start,
+                                        fs,
+                                        None,
+                                        None)
+        if error_code != 0:
+            raise DashboardException(
+                f'Failed to activate snapshot schedule for path {path}: {err}'
+            )
+
+        return f'Snapshot schedule for path {path} activated successfully'
index 0346de67f9419911779115695ddde0bd7ae2e651..f2e93cc742c7af957c7103f9053ad3c04f134a08 100644 (file)
@@ -73,7 +73,7 @@
       [permission]="permissions.cephfs"
       [selection]="selection"
       class="btn-group"
-      [tableActions]="tableActions"
+      [tableActions]="tableActions$ | async"
     >
     </cd-table-actions>
   </div>
index b6b52a15c99d2aaabf6c9a7dfeac2eeffcc962d0..58d3a0cc056afbc4a40210baf8819c3d7689354a 100644 (file)
@@ -26,8 +26,11 @@ import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
 import { NotificationService } from '~/app/shared/services/notification.service';
 import { BlockUI, NgBlockUI } from 'ng-block-ui';
 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
-import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
 import { CephfsSnapshotscheduleFormComponent } from '../cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component';
+import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
+import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
+import { FinishedTask } from '~/app/shared/models/finished-task';
 
 @Component({
   selector: 'cd-cephfs-snapshotschedule-list',
@@ -51,7 +54,7 @@ export class CephfsSnapshotscheduleListComponent
   snapScheduleModuleStatus$ = new BehaviorSubject<boolean>(false);
   moduleServiceListSub!: Subscription;
   columns: CdTableColumn[] = [];
-  tableActions: CdTableAction[] = [];
+  tableActions$ = new BehaviorSubject<CdTableAction[]>([]);
   context!: CdTableFetchDataContext;
   selection = new CdTableSelection();
   permissions!: Permissions;
@@ -59,6 +62,26 @@ export class CephfsSnapshotscheduleListComponent
   errorMessage: string = '';
   selectedName: string = '';
   icons = Icons;
+  tableActions: CdTableAction[] = [
+    {
+      name: this.actionLabels.CREATE,
+      permission: 'create',
+      icon: Icons.add,
+      click: () => this.openModal(false)
+    },
+    {
+      name: this.actionLabels.EDIT,
+      permission: 'update',
+      icon: Icons.edit,
+      click: () => this.openModal(true)
+    },
+    {
+      name: this.actionLabels.DELETE,
+      permission: 'delete',
+      icon: Icons.trash,
+      click: () => this.deleteSnapshotSchedule()
+    }
+  ];
 
   MODULE_NAME = 'snap_schedule';
   ENABLE_MODULE_TIMER = 2 * 1000;
@@ -69,7 +92,8 @@ export class CephfsSnapshotscheduleListComponent
     private modalService: ModalService,
     private mgrModuleService: MgrModuleService,
     private notificationService: NotificationService,
-    private actionLables: ActionLabelsI18n
+    private actionLabels: ActionLabelsI18n,
+    private taskWrapper: TaskWrapperService
   ) {
     super();
     this.permissions = this.authStorageService.getPermissions();
@@ -116,20 +140,7 @@ export class CephfsSnapshotscheduleListComponent
       { prop: 'created', name: $localize`Created`, cellTransformation: CellTemplate.timeAgo }
     ];
 
-    this.tableActions = [
-      {
-        name: this.actionLables.CREATE,
-        permission: 'create',
-        icon: Icons.add,
-        click: () => this.openModal(false)
-      },
-      {
-        name: this.actionLables.EDIT,
-        permission: 'update',
-        icon: Icons.edit,
-        click: () => this.openModal(true)
-      }
-    ];
+    this.tableActions$.next(this.tableActions);
   }
 
   ngOnDestroy(): void {
@@ -142,6 +153,19 @@ export class CephfsSnapshotscheduleListComponent
 
   updateSelection(selection: CdTableSelection) {
     this.selection = selection;
+    if (!this.selection.hasSelection) return;
+    const isActive = this.selection.first()?.active;
+
+    this.tableActions$.next([
+      ...this.tableActions,
+      {
+        name: isActive ? this.actionLabels.DEACTIVATE : this.actionLabels.ACTIVATE,
+        permission: 'update',
+        icon: isActive ? Icons.warning : Icons.success,
+        click: () =>
+          isActive ? this.deactivateSnapshotSchedule() : this.activateSnapshotSchedule()
+      }
+    ]);
   }
 
   openModal(edit = false) {
@@ -204,4 +228,66 @@ export class CephfsSnapshotscheduleListComponent
       }
     );
   }
+
+  deactivateSnapshotSchedule() {
+    const { path, start, fs, schedule } = this.selection.first();
+
+    this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+      itemDescription: $localize`snapshot schedule`,
+      actionDescription: this.actionLabels.DEACTIVATE,
+      submitActionObservable: () =>
+        this.taskWrapper.wrapTaskAroundCall({
+          task: new FinishedTask('cephfs/snapshot/schedule/deactivate', {
+            path
+          }),
+          call: this.snapshotScheduleService.deactivate({
+            path,
+            schedule,
+            start,
+            fs
+          })
+        })
+    });
+  }
+
+  activateSnapshotSchedule() {
+    const { path, start, fs, schedule } = this.selection.first();
+
+    this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+      itemDescription: $localize`snapshot schedule`,
+      actionDescription: this.actionLabels.ACTIVATE,
+      submitActionObservable: () =>
+        this.taskWrapper.wrapTaskAroundCall({
+          task: new FinishedTask('cephfs/snapshot/schedule/activate', {
+            path
+          }),
+          call: this.snapshotScheduleService.activate({
+            path,
+            schedule,
+            start,
+            fs
+          })
+        })
+    });
+  }
+
+  deleteSnapshotSchedule() {
+    const { path, start, fs, schedule } = this.selection.first();
+
+    this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+      itemDescription: $localize`snapshot schedule`,
+      submitActionObservable: () =>
+        this.taskWrapper.wrapTaskAroundCall({
+          task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.DELETE, {
+            path
+          }),
+          call: this.snapshotScheduleService.delete({
+            path,
+            schedule,
+            start,
+            fs
+          })
+        })
+    });
+  }
 }
index 0719089c249f2590763f04e21eff29ebd1db5d3a..9e07f6057ac1d4d2e84e180628a5a43476c7551a 100644 (file)
@@ -19,14 +19,38 @@ export class CephfsSnapshotScheduleService {
     return this.http.post(`${this.baseURL}/snapshot/schedule`, data, { observe: 'response' });
   }
 
-  update(data: Record<string, any>): Observable<any> {
+  update({ fs, path, ...rest }: Record<string, any>): Observable<any> {
     return this.http.put(
-      `${this.baseURL}/snapshot/schedule/${data.fs}/${encodeURIComponent(data.path)}`,
-      data,
+      `${this.baseURL}/snapshot/schedule/${fs}/${encodeURIComponent(path)}`,
+      rest,
       { observe: 'response' }
     );
   }
 
+  activate({ fs, path, ...rest }: Record<string, any>): Observable<any> {
+    return this.http.post(
+      `${this.baseURL}/snapshot/schedule/${fs}/${encodeURIComponent(path)}/activate`,
+      rest,
+      { observe: 'response' }
+    );
+  }
+
+  deactivate({ fs, path, ...rest }: Record<string, any>): Observable<any> {
+    return this.http.post(
+      `${this.baseURL}/snapshot/schedule/${fs}/${encodeURIComponent(path)}/deactivate`,
+      rest,
+      { observe: 'response' }
+    );
+  }
+
+  delete({ fs, path, schedule, start }: Record<string, any>): Observable<any> {
+    return this.http.delete(
+      `${this.baseURL}/snapshot/schedule/${fs}/${encodeURIComponent(
+        path
+      )}/delete_snapshot?schedule=${schedule}&start=${encodeURIComponent(start)}`
+    );
+  }
+
   checkScheduleExists(
     path: string,
     fs: string,
index 7edce5ff6671d036d8e7a82ce2023b828bb7afae..2cf3f1047bab616c8e04c642c8012548c112ed34 100644 (file)
@@ -141,6 +141,8 @@ export class ActionLabelsI18n {
   IMPORT: any;
   MIGRATE: string;
   START_UPGRADE: string;
+  ACTIVATE: string;
+  DEACTIVATE: string;
 
   constructor() {
     /* Create a new item */
@@ -218,6 +220,9 @@ export class ActionLabelsI18n {
     this.DEMOTE = $localize`Demote`;
 
     this.START_UPGRADE = $localize`Start Upgrade`;
+
+    this.ACTIVATE = $localize`Activate`;
+    this.DEACTIVATE = $localize`Deactivate`;
   }
 }
 
index 84e31efea013777d0923a388594e5cee7d186e72..4fbcc09d090274876d93376afaac75292de5fcbe 100644 (file)
@@ -69,7 +69,17 @@ export class TaskMessageService {
     delete: new TaskMessageOperation($localize`Deleting`, $localize`delete`, $localize`Deleted`),
     add: new TaskMessageOperation($localize`Adding`, $localize`add`, $localize`Added`),
     remove: new TaskMessageOperation($localize`Removing`, $localize`remove`, $localize`Removed`),
-    import: new TaskMessageOperation($localize`Importing`, $localize`import`, $localize`Imported`)
+    import: new TaskMessageOperation($localize`Importing`, $localize`import`, $localize`Imported`),
+    activate: new TaskMessageOperation(
+      $localize`Importing`,
+      $localize`activate`,
+      $localize`Activated`
+    ),
+    deactivate: new TaskMessageOperation(
+      $localize`Importing`,
+      $localize`deactivate`,
+      $localize`Deactivated`
+    )
   };
 
   rbd = {
@@ -393,6 +403,18 @@ export class TaskMessageService {
     ),
     'cephfs/snapshot/schedule/edit': this.newTaskMessage(this.commonOperations.update, (metadata) =>
       this.snapshotSchedule(metadata)
+    ),
+    'cephfs/snapshot/schedule/delete': this.newTaskMessage(
+      this.commonOperations.delete,
+      (metadata) => this.snapshotSchedule(metadata)
+    ),
+    'cephfs/snapshot/schedule/activate': this.newTaskMessage(
+      this.commonOperations.activate,
+      (metadata) => this.snapshotSchedule(metadata)
+    ),
+    'cephfs/snapshot/schedule/deactivate': this.newTaskMessage(
+      this.commonOperations.deactivate,
+      (metadata) => this.snapshotSchedule(metadata)
     )
   };
 
index 8457ecb65c6a1bf9bf3e4f00c0e89f41e5ac502f..1fbaac399aaa9cfc26f77ae2a976a07c7c090f6f 100644 (file)
@@ -1890,6 +1890,153 @@ paths:
       - jwt: []
       tags:
       - CephFSSnapshotSchedule
+  /api/cephfs/snapshot/schedule/{fs}/{path}/activate:
+    post:
+      parameters:
+      - in: path
+        name: fs
+        required: true
+        schema:
+          type: string
+      - in: path
+        name: path
+        required: true
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              properties:
+                schedule:
+                  type: string
+                start:
+                  type: string
+              required:
+              - schedule
+              - start
+              type: object
+      responses:
+        '201':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource created.
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      tags:
+      - CephFSSnapshotSchedule
+  /api/cephfs/snapshot/schedule/{fs}/{path}/deactivate:
+    post:
+      parameters:
+      - in: path
+        name: fs
+        required: true
+        schema:
+          type: string
+      - in: path
+        name: path
+        required: true
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              properties:
+                schedule:
+                  type: string
+                start:
+                  type: string
+              required:
+              - schedule
+              - start
+              type: object
+      responses:
+        '201':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource created.
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      tags:
+      - CephFSSnapshotSchedule
+  /api/cephfs/snapshot/schedule/{fs}/{path}/delete_snapshot:
+    delete:
+      parameters:
+      - in: path
+        name: fs
+        required: true
+        schema:
+          type: string
+      - in: path
+        name: path
+        required: true
+        schema:
+          type: string
+      - in: query
+        name: schedule
+        required: true
+        schema:
+          type: string
+      - in: query
+        name: start
+        required: true
+        schema:
+          type: string
+      responses:
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '204':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource deleted.
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      tags:
+      - CephFSSnapshotSchedule
   /api/cephfs/subvolume:
     post:
       parameters: []