]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-form / rbd-form.component.ts
index be2be1daee53ecaef798dc470ad34c3b0be5c03c..1dc1df0dd3aba8da08c4025e85018572d3cd0df2 100644 (file)
@@ -1,30 +1,29 @@
-import { Component, EventEmitter, OnInit } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { FormControl, ValidatorFn, Validators } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
 
-import { I18n } from '@ngx-translate/i18n-polyfill';
-import * as _ from 'lodash';
-
+import _ from 'lodash';
 import { forkJoin, Observable, ReplaySubject } from 'rxjs';
 import { first, switchMap } from 'rxjs/operators';
 
-import { PoolService } from '../../../shared/api/pool.service';
-import { RbdService } from '../../../shared/api/rbd.service';
-import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
-import { Icons } from '../../../shared/enum/icons.enum';
-import { CdFormGroup } from '../../../shared/forms/cd-form-group';
+import { Pool } from '~/app/ceph/pool/pool';
+import { PoolService } from '~/app/shared/api/pool.service';
+import { RbdService } from '~/app/shared/api/rbd.service';
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { CdForm } from '~/app/shared/forms/cd-form';
+import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
 import {
   RbdConfigurationEntry,
   RbdConfigurationSourceField
-} from '../../../shared/models/configuration';
-import { FinishedTask } from '../../../shared/models/finished-task';
-import { ImageSpec } from '../../../shared/models/image-spec';
-import { Permission } from '../../../shared/models/permissions';
-import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
-import { AuthStorageService } from '../../../shared/services/auth-storage.service';
-import { FormatterService } from '../../../shared/services/formatter.service';
-import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
-import { Pool } from '../../pool/pool';
+} from '~/app/shared/models/configuration';
+import { FinishedTask } from '~/app/shared/models/finished-task';
+import { ImageSpec } from '~/app/shared/models/image-spec';
+import { Permission } from '~/app/shared/models/permissions';
+import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { FormatterService } from '~/app/shared/services/formatter.service';
+import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { RBDImageFormat, RbdModel } from '../rbd-list/rbd-model';
 import { RbdImageFeature } from './rbd-feature.interface';
 import { RbdFormCloneRequestModel } from './rbd-form-clone-request.model';
@@ -34,12 +33,18 @@ import { RbdFormEditRequestModel } from './rbd-form-edit-request.model';
 import { RbdFormMode } from './rbd-form-mode.enum';
 import { RbdFormResponseModel } from './rbd-form-response.model';
 
+class ExternalData {
+  rbd: RbdFormResponseModel;
+  defaultFeatures: string[];
+  pools: Pool[];
+}
+
 @Component({
   selector: 'cd-rbd-form',
   templateUrl: './rbd-form.component.html',
   styleUrls: ['./rbd-form.component.scss']
 })
-export class RbdFormComponent implements OnInit {
+export class RbdFormComponent extends CdForm implements OnInit {
   poolPermission: Permission;
   rbdForm: CdFormGroup;
   getDirtyConfigurationValues: (
@@ -51,14 +56,14 @@ export class RbdFormComponent implements OnInit {
   namespacesByPoolCache = {};
   pools: Array<Pool> = null;
   allPools: Array<Pool> = null;
-  dataPools: Array<string> = null;
-  allDataPools: Array<string> = null;
+  dataPools: Array<Pool> = null;
+  allDataPools: Array<Pool> = [];
   features: { [key: string]: RbdImageFeature };
   featuresList: RbdImageFeature[] = [];
-  initializeConfigData = new EventEmitter<{
+  initializeConfigData = new ReplaySubject<{
     initialData: RbdConfigurationEntry[];
     sourceType: RbdConfigurationSourceField;
-  }>();
+  }>(1);
 
   pool: string;
 
@@ -91,6 +96,7 @@ export class RbdFormComponent implements OnInit {
   action: string;
   resource: string;
   private rbdImage = new ReplaySubject(1);
+  private routerUrl: string;
 
   icons = Icons;
 
@@ -102,47 +108,48 @@ export class RbdFormComponent implements OnInit {
     private formatter: FormatterService,
     private taskWrapper: TaskWrapperService,
     private dimlessBinaryPipe: DimlessBinaryPipe,
-    private i18n: I18n,
     public actionLabels: ActionLabelsI18n,
-    public router: Router
+    private router: Router
   ) {
+    super();
+    this.routerUrl = this.router.url;
     this.poolPermission = this.authStorageService.getPermissions().pool;
-    this.resource = this.i18n('RBD');
+    this.resource = $localize`RBD`;
     this.features = {
       'deep-flatten': {
-        desc: this.i18n('Deep flatten'),
+        desc: $localize`Deep flatten`,
         requires: null,
         allowEnable: false,
         allowDisable: true
       },
       layering: {
-        desc: this.i18n('Layering'),
+        desc: $localize`Layering`,
         requires: null,
         allowEnable: false,
         allowDisable: false
       },
       'exclusive-lock': {
-        desc: this.i18n('Exclusive lock'),
+        desc: $localize`Exclusive lock`,
         requires: null,
         allowEnable: true,
         allowDisable: true
       },
       'object-map': {
-        desc: this.i18n('Object map (requires exclusive-lock)'),
+        desc: $localize`Object map (requires exclusive-lock)`,
         requires: 'exclusive-lock',
         allowEnable: true,
         allowDisable: true,
         initDisabled: true
       },
       journaling: {
-        desc: this.i18n('Journaling (requires exclusive-lock)'),
+        desc: $localize`Journaling (requires exclusive-lock)`,
         requires: 'exclusive-lock',
         allowEnable: true,
         allowDisable: true,
         initDisabled: true
       },
       'fast-diff': {
-        desc: this.i18n('Fast diff (interlocked with object-map)'),
+        desc: $localize`Fast diff (interlocked with object-map)`,
         requires: 'object-map',
         allowEnable: true,
         allowDisable: true,
@@ -221,108 +228,120 @@ export class RbdFormComponent implements OnInit {
   }
 
   ngOnInit() {
-    if (this.router.url.startsWith('/block/rbd/edit')) {
+    this.prepareFormForAction();
+    this.gatherNeededData().subscribe(this.handleExternalData.bind(this));
+  }
+
+  private prepareFormForAction() {
+    const url = this.routerUrl;
+    if (url.startsWith('/block/rbd/edit')) {
       this.mode = this.rbdFormMode.editing;
       this.action = this.actionLabels.EDIT;
       this.disableForEdit();
-    } else if (this.router.url.startsWith('/block/rbd/clone')) {
+    } else if (url.startsWith('/block/rbd/clone')) {
       this.mode = this.rbdFormMode.cloning;
       this.disableForClone();
       this.action = this.actionLabels.CLONE;
-    } else if (this.router.url.startsWith('/block/rbd/copy')) {
+    } else if (url.startsWith('/block/rbd/copy')) {
       this.mode = this.rbdFormMode.copying;
       this.action = this.actionLabels.COPY;
       this.disableForCopy();
     } else {
       this.action = this.actionLabels.CREATE;
     }
-    enum Promisse {
-      RbdServiceGet = 'rbdService.get',
-      PoolServiceList = 'poolService.list'
-    }
-    const promisses = {};
-    if (
-      this.mode === this.rbdFormMode.editing ||
-      this.mode === this.rbdFormMode.cloning ||
-      this.mode === this.rbdFormMode.copying
-    ) {
+    _.each(this.features, (feature) => {
+      this.rbdForm
+        .get('features')
+        .get(feature.key)
+        .valueChanges.subscribe((value) => this.featureFormUpdate(feature.key, value));
+    });
+  }
+
+  private gatherNeededData(): Observable<object> {
+    const promises = {};
+    if (this.mode) {
+      // Mode is not set for creation
       this.route.params.subscribe((params: { image_spec: string; snap: string }) => {
         const imageSpec = ImageSpec.fromString(decodeURIComponent(params.image_spec));
         if (params.snap) {
           this.snapName = decodeURIComponent(params.snap);
         }
-        promisses[Promisse.RbdServiceGet] = this.rbdService.get(imageSpec);
+        promises['rbd'] = this.rbdService.get(imageSpec);
       });
     } else {
       // New image
-      this.rbdService.defaultFeatures().subscribe((defaultFeatures: Array<string>) => {
-        this.setFeatures(defaultFeatures);
-      });
+      promises['defaultFeatures'] = this.rbdService.defaultFeatures();
     }
     if (this.mode !== this.rbdFormMode.editing && this.poolPermission.read) {
-      promisses[Promisse.PoolServiceList] = this.poolService.list([
+      promises['pools'] = this.poolService.list([
         'pool_name',
         'type',
         'flags_names',
         'application_metadata'
       ]);
     }
+    return forkJoin(promises);
+  }
 
-    forkJoin(promisses).subscribe((data: object) => {
-      // poolService.list
-      if (data[Promisse.PoolServiceList]) {
-        const pools: Pool[] = [];
-        const dataPools = [];
-        for (const pool of data[Promisse.PoolServiceList]) {
-          if (this.rbdService.isRBDPool(pool)) {
-            if (pool.type === 'replicated') {
-              pools.push(pool);
-              dataPools.push(pool);
-            } else if (
-              pool.type === 'erasure' &&
-              pool.flags_names.indexOf('ec_overwrites') !== -1
-            ) {
-              dataPools.push(pool);
-            }
-          }
-        }
-        this.pools = pools;
-        this.allPools = pools;
-        this.dataPools = dataPools;
-        this.allDataPools = dataPools;
-        if (this.pools.length === 1) {
-          const poolName = this.pools[0].pool_name;
-          this.rbdForm.get('pool').setValue(poolName);
-          this.onPoolChange(poolName);
-        }
-      }
+  private handleExternalData(data: ExternalData) {
+    this.handlePoolData(data.pools);
 
-      // rbdService.get
-      if (data[Promisse.RbdServiceGet]) {
-        const resp: RbdFormResponseModel = data[Promisse.RbdServiceGet];
-        this.setResponse(resp, this.snapName);
-        this.rbdImage.next(resp);
-      }
-    });
+    if (data.defaultFeatures) {
+      // Fetched only during creation
+      this.setFeatures(data.defaultFeatures);
+    }
 
-    _.each(this.features, (feature) => {
-      this.rbdForm
-        .get('features')
-        .get(feature.key)
-        .valueChanges.subscribe((value) => this.featureFormUpdate(feature.key, value));
-    });
+    if (data.rbd) {
+      // Not fetched for creation
+      const resp = data.rbd;
+      this.setResponse(resp, this.snapName);
+      this.rbdImage.next(resp);
+    }
+
+    this.loadingReady();
+  }
+
+  private handlePoolData(data: Pool[]) {
+    if (!data) {
+      // Not fetched while editing
+      return;
+    }
+    const pools: Pool[] = [];
+    const dataPools = [];
+    for (const pool of data) {
+      if (this.rbdService.isRBDPool(pool)) {
+        if (pool.type === 'replicated') {
+          pools.push(pool);
+          dataPools.push(pool);
+        } else if (pool.type === 'erasure' && pool.flags_names.indexOf('ec_overwrites') !== -1) {
+          dataPools.push(pool);
+        }
+      }
+    }
+    this.pools = pools;
+    this.allPools = pools;
+    this.dataPools = dataPools;
+    this.allDataPools = dataPools;
+    if (this.pools.length === 1) {
+      const poolName = this.pools[0].pool_name;
+      this.rbdForm.get('pool').setValue(poolName);
+      this.onPoolChange(poolName);
+    }
+    if (this.allDataPools.length <= 1) {
+      this.rbdForm.get('useDataPool').disable();
+    }
   }
 
   onPoolChange(selectedPoolName: string) {
-    const newDataPools = this.allDataPools
+    const dataPoolControl = this.rbdForm.get('dataPool');
+    if (dataPoolControl.value === selectedPoolName) {
+      dataPoolControl.setValue(null);
+    }
+    this.dataPools = this.allDataPools
       ? this.allDataPools.filter((dataPool: any) => {
           return dataPool.pool_name !== selectedPoolName;
         })
       : [];
-    if (this.rbdForm.getValue('dataPool') === selectedPoolName) {
-      this.rbdForm.get('dataPool').setValue(null);
-    }
-    this.dataPools = newDataPools;
     this.namespaces = null;
     if (selectedPoolName in this.namespacesByPoolCache) {
       this.namespaces = this.namespacesByPoolCache[selectedPoolName];
@@ -405,12 +424,8 @@ export class RbdFormComponent implements OnInit {
     };
   }
 
-  protected getDependendChildFeatures(featureKey: string) {
-    return _.filter(this.features, (f) => f.requires === featureKey) || [];
-  }
-
   deepBoxCheck(key: string, checked: boolean) {
-    const childFeatures = this.getDependendChildFeatures(key);
+    const childFeatures = this.getDependentChildFeatures(key);
     childFeatures.forEach((feature) => {
       const featureControl = this.rbdForm.get(feature.key);
       if (checked) {
@@ -435,6 +450,10 @@ export class RbdFormComponent implements OnInit {
     });
   }
 
+  protected getDependentChildFeatures(featureKey: string) {
+    return _.filter(this.features, (f) => f.requires === featureKey) || [];
+  }
+
   interlockCheck(key: string, checked: boolean) {
     // Adds a compatibility layer for Ceph cluster where the feature interlock of features hasn't
     // been implemented yet. It disables the feature interlock for images which only have one of
@@ -539,9 +558,8 @@ export class RbdFormComponent implements OnInit {
       .get('stripingUnit')
       .setValue(this.dimlessBinaryPipe.transform(response.stripe_unit));
     this.rbdForm.get('stripingCount').setValue(response.stripe_count);
-
     /* Configuration */
-    this.initializeConfigData.emit({
+    this.initializeConfigData.next({
       initialData: this.response.configuration,
       sourceType: RbdConfigurationSourceField.image
     });
@@ -553,6 +571,14 @@ export class RbdFormComponent implements OnInit {
     request.namespace = this.rbdForm.getValue('namespace');
     request.name = this.rbdForm.getValue('name');
     request.size = this.formatter.toBytes(this.rbdForm.getValue('size'));
+    this.addObjectSizeAndStripingToRequest(request);
+    request.configuration = this.getDirtyConfigurationValues();
+    return request;
+  }
+
+  private addObjectSizeAndStripingToRequest(
+    request: RbdFormCreateRequestModel | RbdFormCloneRequestModel | RbdFormCopyRequestModel
+  ) {
     request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size'));
     _.forIn(this.features, (feature) => {
       if (this.rbdForm.getValue(feature.key)) {
@@ -564,11 +590,6 @@ export class RbdFormComponent implements OnInit {
     request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit'));
     request.stripe_count = this.rbdForm.getValue('stripingCount');
     request.data_pool = this.rbdForm.getValue('dataPool');
-
-    /* Configuration */
-    request.configuration = this.getDirtyConfigurationValues();
-
-    return request;
   }
 
   createAction(): Observable<any> {
@@ -592,9 +613,7 @@ export class RbdFormComponent implements OnInit {
         request.features.push(feature.key);
       }
     });
-
     request.configuration = this.getDirtyConfigurationValues();
-
     return request;
   }
 
@@ -603,24 +622,11 @@ export class RbdFormComponent implements OnInit {
     request.child_pool_name = this.rbdForm.getValue('pool');
     request.child_namespace = this.rbdForm.getValue('namespace');
     request.child_image_name = this.rbdForm.getValue('name');
-    request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size'));
-    _.forIn(this.features, (feature) => {
-      if (this.rbdForm.getValue(feature.key)) {
-        request.features.push(feature.key);
-      }
-    });
-
-    /* Striping */
-    request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit'));
-    request.stripe_count = this.rbdForm.getValue('stripingCount');
-    request.data_pool = this.rbdForm.getValue('dataPool');
-
-    /* Configuration */
+    this.addObjectSizeAndStripingToRequest(request);
     request.configuration = this.getDirtyConfigurationValues(
       true,
       RbdConfigurationSourceField.image
     );
-
     return request;
   }
 
@@ -665,24 +671,11 @@ export class RbdFormComponent implements OnInit {
     request.dest_pool_name = this.rbdForm.getValue('pool');
     request.dest_namespace = this.rbdForm.getValue('namespace');
     request.dest_image_name = this.rbdForm.getValue('name');
-    request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size'));
-    _.forIn(this.features, (feature) => {
-      if (this.rbdForm.getValue(feature.key)) {
-        request.features.push(feature.key);
-      }
-    });
-
-    /* Striping */
-    request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit'));
-    request.stripe_count = this.rbdForm.getValue('stripingCount');
-    request.data_pool = this.rbdForm.getValue('dataPool');
-
-    /* Configuration */
+    this.addObjectSizeAndStripingToRequest(request);
     request.configuration = this.getDirtyConfigurationValues(
       true,
       RbdConfigurationSourceField.image
     );
-
     return request;
   }
 
@@ -724,7 +717,7 @@ export class RbdFormComponent implements OnInit {
         })
       )
       .subscribe(
-        () => {},
+        () => undefined,
         () => this.rbdForm.setErrors({ cdSubmitButton: true }),
         () => this.router.navigate(['/block/rbd'])
       );