]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add UI for RBD Trash Delete
authorTiago Melo <tmelo@suse.com>
Tue, 3 Jul 2018 09:58:03 +0000 (10:58 +0100)
committerTiago Melo <tspmelo@gmail.com>
Tue, 25 Sep 2018 13:02:58 +0000 (14:02 +0100)
Signed-off-by: Tiago Melo <tmelo@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/deletion-modal/deletion-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/deletion-modal/deletion-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts

index 51a2be256be9b441730acf9c489970636ba444ae..e033ab841ff9b5eb24f7bcbfdcf3ac1037434324 100644 (file)
 
   {{ value | cdDate }}
 </ng-template>
+
+<ng-template #deleteTpl
+             let-expiresAt>
+  <p class="text-danger"
+     *ngIf="!isExpired(expiresAt)"
+     i18n>
+    <strong>This image is protected until {{ expiresAt | cdDate }}.</strong>
+  </p>
+</ng-template>
index 1bb18d2bdaecbddff71c6715988abeb5192a6810..26dbdb66d594f7e6cc1aa1d1b1bc57ad967f5545 100644 (file)
@@ -93,5 +93,13 @@ describe('RbdTrashListComponent', () => {
       expect(component.images.length).toBe(2);
       expect(component.images.every((image) => !image.cdExecuting)).toBeTruthy();
     });
+
+    it('should show when an existing image is being modified', () => {
+      addTask('rbd/trash/remove', '1');
+      addTask('rbd/trash/restore', '2');
+      expect(component.images.length).toBe(2);
+      expectImageTasks(component.images[0], 'Deleting');
+      expectImageTasks(component.images[1], 'Restoring');
+    });
   });
 });
index e4474dd2c22a9e4e5177a52a65e821035482add7..6f0f6437c6f72cd14e0162a26ed3978ace27c846 100644 (file)
@@ -5,6 +5,7 @@ import * as moment from 'moment';
 import { BsModalRef, BsModalService } from 'ngx-bootstrap';
 
 import { RbdService } from '../../../shared/api/rbd.service';
+import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component';
 import { TableComponent } from '../../../shared/datatable/table/table.component';
 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
@@ -12,10 +13,12 @@ import { CdTableAction } from '../../../shared/models/cd-table-action';
 import { CdTableColumn } from '../../../shared/models/cd-table-column';
 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
 import { ExecutingTask } from '../../../shared/models/executing-task';
+import { FinishedTask } from '../../../shared/models/finished-task';
 import { Permission } from '../../../shared/models/permissions';
 import { CdDatePipe } from '../../../shared/pipes/cd-date.pipe';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
 import { TaskListService } from '../../../shared/services/task-list.service';
+import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
 import { RbdTrashRestoreModalComponent } from '../rbd-trash-restore-modal/rbd-trash-restore-modal.component';
 
 @Component({
@@ -29,6 +32,8 @@ export class RbdTrashListComponent implements OnInit {
   table: TableComponent;
   @ViewChild('expiresTpl')
   expiresTpl: TemplateRef<any>;
+  @ViewChild('deleteTpl')
+  deleteTpl: TemplateRef<any>;
 
   columns: CdTableColumn[];
   executingTasks: ExecutingTask[] = [];
@@ -45,7 +50,8 @@ export class RbdTrashListComponent implements OnInit {
     private rbdService: RbdService,
     private modalService: BsModalService,
     private cdDatePipe: CdDatePipe,
-    private taskListService: TaskListService
+    private taskListService: TaskListService,
+    private taskWrapper: TaskWrapperService
   ) {
     this.permission = this.authStorageService.getPermissions().rbdImage;
 
@@ -55,7 +61,13 @@ export class RbdTrashListComponent implements OnInit {
       click: () => this.restoreModal(),
       name: 'Restore'
     };
-    this.tableActions = [restoreAction];
+    const deleteAction: CdTableAction = {
+      permission: 'delete',
+      icon: 'fa-times',
+      click: () => this.deleteModal(),
+      name: 'Delete'
+    };
+    this.tableActions = [restoreAction, deleteAction];
   }
 
   ngOnInit() {
@@ -157,4 +169,32 @@ export class RbdTrashListComponent implements OnInit {
 
     this.modalRef = this.modalService.show(RbdTrashRestoreModalComponent, { initialState });
   }
+
+  deleteModal() {
+    const poolName = this.selection.first().pool_name;
+    const imageName = this.selection.first().name;
+    const imageId = this.selection.first().id;
+    const expiresAt = this.selection.first().deferment_end_time;
+
+    this.modalRef = this.modalService.show(DeletionModalComponent, {
+      initialState: {
+        itemDescription: 'RBD',
+        bodyTemplate: this.deleteTpl,
+        bodyContext: { $implicit: expiresAt },
+        submitActionObservable: () =>
+          this.taskWrapper.wrapTaskAroundCall({
+            task: new FinishedTask('rbd/trash/remove', {
+              pool_name: poolName,
+              image_id: imageId,
+              image_name: imageName
+            }),
+            call: this.rbdService.removeTrash(poolName, imageId, imageName, true)
+          })
+      }
+    });
+  }
+
+  isExpired(expiresAt): boolean {
+    return moment().isAfter(expiresAt);
+  }
 }
index 97efc6d331e359c797a9b1cdf863c55c82437ffc..765d2054f8687af17eacc9ac63647a0bdd8f688b 100644 (file)
@@ -115,4 +115,11 @@ export class RbdService {
       { observe: 'response' }
     );
   }
+
+  removeTrash(poolName, imageId, imageName, force = false) {
+    return this.http.delete(
+      `api/block/image/trash/${poolName}/${imageId}/?image_name=${imageName}&force=${force}`,
+      { observe: 'response' }
+    );
+  }
 }
index ee4532677f94b78f2b6746a46acc368795839f3a..d12d9006bba23c52c25e619553a9beff5ce2cbf5 100644 (file)
@@ -10,7 +10,7 @@
           [formGroup]="deletionForm"
           novalidate>
       <div class="modal-body">
-        <ng-container *ngTemplateOutlet="bodyTemplate"></ng-container>
+        <ng-container *ngTemplateOutlet="bodyTemplate; context: bodyContext"></ng-container>
         <div class="question">
           <p i18n>
             Are you sure that you want to {{ actionDescription | lowercase }} the selected {{ itemDescription }}?
index f472baccbb7d7dbab2984c092a9b0d0955b9353a..3206503674024d2884c3cf9f8e4a9aca451ac1ec 100644 (file)
@@ -16,6 +16,7 @@ export class DeletionModalComponent implements OnInit {
   @ViewChild(SubmitButtonComponent)
   submitButton: SubmitButtonComponent;
   bodyTemplate: TemplateRef<any>;
+  bodyContext: any;
   submitActionObservable: () => Observable<any>;
   submitAction: Function;
   deletionForm: CdFormGroup;
index 45f9a07b9c9622f14f782cb383214250878a1ffe..517b0865913fcb640c61e8e283dc18eb20a8eea5 100644 (file)
@@ -142,6 +142,20 @@ export class TaskMessageService {
       (metadata) => ({
         17: `Image name '${metadata.pool_name}/${metadata.new_image_name}' is already in use.`
       })
+    ),
+    'rbd/trash/remove': new TaskMessage(
+      new TaskMessageOperation('Deleting', 'delete', 'Deleted'),
+      (metadata) => `image '${metadata.pool_name}/${metadata.image_name}@${metadata.image_id}'`
+    ),
+    'rbd/trash/purge': new TaskMessage(
+      new TaskMessageOperation('Purging', 'purge', 'Purged'),
+      (metadata) => {
+        let message = 'all pools';
+        if (metadata.pool_name) {
+          message = `'${metadata.pool_name}'`;
+        }
+        return `images from ${message}`;
+      }
     )
   };