]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: RBD copy (frontend)
authorRicardo Marques <rimarques@suse.com>
Thu, 19 Apr 2018 16:54:45 +0000 (17:54 +0100)
committerRicardo Marques <rimarques@suse.com>
Tue, 24 Apr 2018 15:50:37 +0000 (16:50 +0100)
Signed-off-by: Ricardo Marques <rimarques@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form-copy-request.model.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form-mode.enum.ts
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.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager-message.service.ts

index a015eebe05050f9c82b1b5d1fc83fef761f5b698..6bb94b7425d047ec6e88ee11483b933f14f6eaa0 100644 (file)
@@ -54,6 +54,11 @@ const routes: Routes = [
     component: RbdFormComponent,
     canActivate: [AuthGuardService]
   },
+  {
+    path: 'rbd/copy/:pool/:name',
+    component: RbdFormComponent,
+    canActivate: [AuthGuardService]
+  },
   {
     path: 'perf_counters/:type/:id',
     component: PerformanceCounterComponent,
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form-copy-request.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form-copy-request.model.ts
new file mode 100644 (file)
index 0000000..9294583
--- /dev/null
@@ -0,0 +1,9 @@
+export class RbdFormCopyRequestModel {
+  dest_pool_name: string;
+  dest_image_name: string;
+  obj_size: number;
+  features: Array<string> = [];
+  stripe_unit: number;
+  stripe_count: number;
+  data_pool: string;
+}
index c89005fbc19dd8a599f6337415a9bceeabb80989..3db18a1d688589e89b9346c9e7297ea15940deb9 100644 (file)
@@ -1,4 +1,5 @@
 export enum RbdFormMode {
   editing = 'editing',
-  cloning = 'cloning'
+  cloning = 'cloning',
+  copying = 'copying'
 }
index 71bcda73c69bb87c6832e643bfa9044363c6ab55..6fc7b86615a12b817ee667c586afa0e733927188 100644 (file)
@@ -4,7 +4,7 @@
     <li class="breadcrumb-item">
       <a routerLink="/block/rbd">Images</a></li>
     <li class="breadcrumb-item active"
-        i18n>{mode, select, editing {Edit} cloning {Clone} other {Add}}</li>
+        i18n>{mode, select, editing {Edit} cloning {Clone} copying {Copy} other {Add}}</li>
   </ol>
 </nav>
 
@@ -17,7 +17,7 @@
     <div class="panel panel-default">
       <div class="panel-heading">
         <h3 class="panel-title">
-          <span i18n>{mode, select, editing {Edit} cloning {Clone} other {Add}}</span> RBD
+          <span i18n>{mode, select, editing {Edit} cloning {Clone} copying {Copy} other {Add}}</span> RBD
         </h3>
       </div>
       <div class="panel-body">
@@ -27,7 +27,7 @@
              *ngIf="rbdForm.controls.parent.value">
           <label i18n
                  class="control-label col-sm-3"
-                 for="name">{mode, select, cloning {Clone from} other {Parent}}
+                 for="name">{mode, select, cloning {Clone from} copying {Copy from} other {Parent}}
           </label>
           <div class="col-sm-9">
             <input class="form-control"
           <cd-submit-button [form]="rbdForm"
                             type="button"
                             (submitAction)="submit()">
-            <span i18n>{mode, select, editing {Update} cloning {Clone} other {Create}}</span> RBD
+            <span i18n>{mode, select, editing {Update} cloning {Clone} copying {Copy} other {Create}}</span> RBD
           </cd-submit-button>
           <button i18n
                   type="button"
index 5eb224eb7cd1a59de75d490c219303a426d8421c..59aba9a8bc79158968426ac00ec63713eda97306 100644 (file)
@@ -14,6 +14,7 @@ import { NotificationService } from '../../../shared/services/notification.servi
 import { TaskManagerMessageService } from '../../../shared/services/task-manager-message.service';
 import { TaskManagerService } from '../../../shared/services/task-manager.service';
 import { RbdFormCloneRequestModel } from './rbd-form-clone-request.model';
+import { RbdFormCopyRequestModel } from './rbd-form-copy-request.model';
 import { RbdFormCreateRequestModel } from './rbd-form-create-request.model';
 import { RbdFormEditRequestModel } from './rbd-form-edit-request.model';
 import { RbdFormMode } from './rbd-form-mode.enum';
@@ -186,6 +187,11 @@ export class RbdFormComponent implements OnInit {
     this.rbdForm.get('size').disable();
   }
 
+  disableForCopy() {
+    this.rbdForm.get('parent').disable();
+    this.rbdForm.get('size').disable();
+  }
+
   ngOnInit() {
     if (this.router.url.startsWith('/rbd/edit')) {
       this.mode = this.rbdFormMode.editing;
@@ -193,8 +199,13 @@ export class RbdFormComponent implements OnInit {
     } else if (this.router.url.startsWith('/rbd/clone')) {
       this.mode = this.rbdFormMode.cloning;
       this.disableForClone();
+    } else if (this.router.url.startsWith('/rbd/copy')) {
+      this.mode = this.rbdFormMode.copying;
+      this.disableForCopy();
     }
-    if (this.mode === this.rbdFormMode.editing || this.mode === this.rbdFormMode.cloning) {
+    if (this.mode === this.rbdFormMode.editing ||
+        this.mode === this.rbdFormMode.cloning ||
+        this.mode === this.rbdFormMode.copying) {
       this.routeParamsSubscribe = this.route.params.subscribe(
         (params: { pool: string, name: string, snap: string }) => {
           const poolName = params.pool;
@@ -378,6 +389,8 @@ export class RbdFormComponent implements OnInit {
     this.response = response;
     if (this.mode === this.rbdFormMode.cloning) {
       this.rbdForm.get('parent').setValue(`${response.pool_name}/${response.name}@${snapName}`);
+    } else if (this.mode === this.rbdFormMode.copying) {
+      this.rbdForm.get('parent').setValue(`${response.pool_name}/${response.name}`);
     } else if (response.parent) {
       const parent = response.parent;
       this.rbdForm.get('parent')
@@ -553,11 +566,66 @@ export class RbdFormComponent implements OnInit {
       });
   }
 
+  copyRequest(): RbdFormCopyRequestModel {
+    const request = new RbdFormCopyRequestModel();
+    request.dest_pool_name = this.rbdForm.get('pool').value;
+    request.dest_image_name = this.rbdForm.get('name').value;
+    request.obj_size = this.formatter.toBytes(this.rbdForm.get('obj_size').value);
+    if (!this.defaultFeaturesFormControl.value) {
+      _.forIn(this.features, (feature) => {
+        if (this.featuresFormGroups.get(feature.key).value) {
+          request.features.push(feature.key);
+        }
+      });
+    } else {
+      request.features = null;
+    }
+    request.stripe_unit = this.formatter.toBytes(this.rbdForm.get('stripingUnit').value);
+    request.stripe_count = this.rbdForm.get('stripingCount').value;
+    request.data_pool = this.rbdForm.get('dataPool').value;
+    return request;
+  }
+
+  copyAction() {
+    const request = this.copyRequest();
+    const finishedTask = new FinishedTask();
+    finishedTask.name = 'rbd/copy';
+    finishedTask.metadata = {
+      'src_pool_name': this.response.pool_name,
+      'src_image_name': this.response.name,
+      'dest_pool_name': request.dest_pool_name,
+      'dest_image_name': request.dest_image_name
+    };
+    this.rbdService.copy(this.response.pool_name, this.response.name, request)
+      .toPromise().then((resp) => {
+        if (resp.status === 202) {
+          this.notificationService.show(NotificationType.info,
+            `RBD copy in progress...`,
+            this.taskManagerMessageService.getDescription(finishedTask));
+          this.taskManagerService.subscribe(finishedTask.name, finishedTask.metadata,
+            (asyncFinishedTask: FinishedTask) => {
+              this.notificationService.notifyTask(asyncFinishedTask);
+            });
+        } else {
+          finishedTask.success = true;
+          this.notificationService.notifyTask(finishedTask);
+        }
+        this.router.navigate(['/block/rbd']);
+      }).catch((resp) => {
+        this.rbdForm.setErrors({'cdSubmitButton': true});
+        finishedTask.success = false;
+        finishedTask.exception = resp.error;
+        this.notificationService.notifyTask(finishedTask);
+      });
+  }
+
   submit() {
     if (this.mode === this.rbdFormMode.editing) {
       this.editAction();
     } else if (this.mode === this.rbdFormMode.cloning) {
       this.cloneAction();
+    } else if (this.mode === this.rbdFormMode.copying) {
+      this.copyAction();
     } else {
       this.createAction();
     }
index dbdd3f3d47292f9f060aa62b38e8bcf93fcbfb52..b7324eade489d5be777c8a2644eb5bed9e929144 100644 (file)
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
           <a class="dropdown-item" routerLink="/rbd/edit/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}"><i class="fa fa-fw fa-pencil"></i><span i18n>Edit</span></a>
         </li>
+        <li role="menuitem"
+            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
+          <a class="dropdown-item" routerLink="/rbd/copy/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}"><i class="fa fa-fw fa-copy"></i><span i18n>Copy</span></a>
+        </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
           <a class="dropdown-item" (click)="deleteRbdModal()"><i class="fa fa-fw fa-trash-o"></i><span i18n>Delete</span></a>
index e223ff9f7c6b49429db3dca669c69669cfea6e5d..febdd22da6fa7b87937c2bf09b8e4d8fba08597b 100644 (file)
@@ -208,6 +208,13 @@ export class RbdListComponent implements OnInit, OnDestroy {
         rbdModel.pool_name = executingTask.metadata['child_pool_name'];
         rbdModel.cdExecuting = 'cloning';
         resultRBDs.push(rbdModel);
+
+      } else if (executingTask.name === 'rbd/copy') {
+        const rbdModel = new RbdModel();
+        rbdModel.name = executingTask.metadata['dest_image_name'];
+        rbdModel.pool_name = executingTask.metadata['dest_pool_name'];
+        rbdModel.cdExecuting = 'copying';
+        resultRBDs.push(rbdModel);
       }
     });
     return resultRBDs;
index f01cbaad6354dc83dabe9cebe47071623008a68b..855f829f9db20ae821f16e97c3676c2b911c37d2 100644 (file)
@@ -27,6 +27,11 @@ export class RbdService {
     return this.http.get('api/block/image');
   }
 
+  copy(poolName, rbdName, rbd) {
+    return this.http.post(`api/block/image/${poolName}/${rbdName}/copy`, rbd,
+      { observe: 'response' });
+  }
+
   createSnapshot(poolName, rbdName, snapshotName) {
     const request = {
       snapshot_name: snapshotName
index f4fa85dc0377e5b829413813449745d991a1d52f..6fcb041c13d23debcce27e2260ba214f591fcf9c 100644 (file)
@@ -64,6 +64,17 @@ export class TaskManagerMessageService {
         };
       }
     ),
+    'rbd/copy': new TaskManagerMessage(
+      (metadata) => `Copy RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}'`,
+      (metadata) => `RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}'
+                     has been copied successfully`,
+      (metadata) => {
+        return {
+          '17': `Name '${metadata.child_pool_name}/${metadata.child_image_name}' is already
+                 in use.`
+        };
+      }
+    ),
     'rbd/snap/create': new TaskManagerMessage(
       (metadata) => `Create snapshot ` +
                     `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,