File

src/app/ceph/block/rbd-form/rbd-form.component.ts

Implements

OnInit

Metadata

selector cd-rbd-form
styleUrls ./rbd-form.component.scss
templateUrl ./rbd-form.component.html

Index

Properties
Methods

Constructor

constructor(authStorageService: AuthStorageService, route: ActivatedRoute, router: Router, poolService: PoolService, rbdService: RbdService, formatter: FormatterService, taskWrapper: TaskWrapperService, dimlessBinaryPipe: DimlessBinaryPipe, i18n: I18n, actionLabels: ActionLabelsI18n)
Parameters :
Name Type Optional
authStorageService AuthStorageService No
route ActivatedRoute No
router Router No
poolService PoolService No
rbdService RbdService No
formatter FormatterService No
taskWrapper TaskWrapperService No
dimlessBinaryPipe DimlessBinaryPipe No
i18n I18n No
actionLabels ActionLabelsI18n No

Methods

cloneAction
cloneAction()
Returns : Observable<any>
cloneRequest
cloneRequest()
copyAction
copyAction()
Returns : Observable<any>
copyRequest
copyRequest()
createAction
createAction()
Returns : Observable<any>
createForm
createForm()
Returns : void
createRequest
createRequest()
Returns : any
deepBoxCheck
deepBoxCheck(key, checked)
Parameters :
Name Optional
key No
checked No
Returns : void
disableForClone
disableForClone()
Returns : void
disableForCopy
disableForCopy()
Returns : void
disableForEdit
disableForEdit()
Returns : void
editAction
editAction()
Returns : Observable<any>
editRequest
editRequest()
Returns : any
featureFormUpdate
featureFormUpdate(key, checked)
Parameters :
Name Optional
key No
checked No
Returns : void
ngOnInit
ngOnInit()
Returns : void
onDataPoolChange
onDataPoolChange(selectedDataPoolName)
Parameters :
Name Optional
selectedDataPoolName No
Returns : void
onPoolChange
onPoolChange(selectedPoolName)
Parameters :
Name Optional
selectedPoolName No
Returns : void
onUseDataPoolChange
onUseDataPoolChange()
Returns : void
setFeatures
setFeatures(features: Array)
Parameters :
Name Type Optional
features Array<string> No
Returns : void
setResponse
setResponse(response: RbdFormResponseModel, snapName: string)
Parameters :
Name Type Optional
response RbdFormResponseModel No
snapName string No
Returns : void
submit
submit()
Returns : void
validateRbdForm
validateRbdForm(formatter: FormatterService)
Parameters :
Name Type Optional
formatter FormatterService No
Returns : ValidatorFn
watchDataFeatures
watchDataFeatures(key, checked)
Parameters :
Name Optional
key No
checked No
Returns : void

Properties

action
Type : string
Public actionLabels
Type : ActionLabelsI18n
advancedEnabled
Default value : false
allDataPools
Type : Array<string>
Default value : null
allPools
Type : Array<string>
Default value : null
dataPools
Type : Array<string>
Default value : null
deepFlattenFormControl
Type : FormControl
defaultObjectSize
Type : string
Default value : '4 MiB'
exclusiveLockFormControl
Type : FormControl
fastDiffFormControl
Type : FormControl
features
Type : any
featuresFormGroups
Type : CdFormGroup
featuresList
Type : []
Default value : []
getDirtyConfigurationValues
Type : function
initializeConfigData
Default value : new EventEmitter<{ initialData: RbdConfigurationEntry[]; sourceType: RbdConfigurationSourceField; }>()
journalingFormControl
Type : FormControl
layeringFormControl
Type : FormControl
mode
Type : RbdFormMode
objectMapFormControl
Type : FormControl
objectSizes
Type : Array<string>
Default value : [ '4 KiB', '8 KiB', '16 KiB', '32 KiB', '64 KiB', '128 KiB', '256 KiB', '512 KiB', '1 MiB', '2 MiB', '4 MiB', '8 MiB', '16 MiB', '32 MiB' ]
pool
Type : string
poolPermission
Type : Permission
pools
Type : Array<string>
Default value : null
rbdForm
Type : CdFormGroup
Public rbdFormMode
Default value : RbdFormMode
resource
Type : string
response
Type : RbdFormResponseModel
snapName
Type : string
import { Component, EventEmitter, 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 { Observable } from 'rxjs';

import { PoolService } from '../../../shared/api/pool.service';
import { RbdService } from '../../../shared/api/rbd.service';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { CdFormGroup } from '../../../shared/forms/cd-form-group';
import {
  RbdConfigurationEntry,
  RbdConfigurationSourceField
} from '../../../shared/models/configuration';
import { FinishedTask } from '../../../shared/models/finished-task';
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 { 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';
import { RbdFormResponseModel } from './rbd-form-response.model';

@Component({
  selector: 'cd-rbd-form',
  templateUrl: './rbd-form.component.html',
  styleUrls: ['./rbd-form.component.scss']
})
export class RbdFormComponent implements OnInit {
  poolPermission: Permission;
  rbdForm: CdFormGroup;
  featuresFormGroups: CdFormGroup;
  deepFlattenFormControl: FormControl;
  layeringFormControl: FormControl;
  exclusiveLockFormControl: FormControl;
  objectMapFormControl: FormControl;
  journalingFormControl: FormControl;
  fastDiffFormControl: FormControl;
  getDirtyConfigurationValues: (
    includeLocalField?: boolean,
    localField?: RbdConfigurationSourceField
  ) => RbdConfigurationEntry[];

  pools: Array<string> = null;
  allPools: Array<string> = null;
  dataPools: Array<string> = null;
  allDataPools: Array<string> = null;
  features: any;
  featuresList = [];
  initializeConfigData = new EventEmitter<{
    initialData: RbdConfigurationEntry[];
    sourceType: RbdConfigurationSourceField;
  }>();

  pool: string;

  advancedEnabled = false;

  public rbdFormMode = RbdFormMode;
  mode: RbdFormMode;

  response: RbdFormResponseModel;
  snapName: string;

  defaultObjectSize = '4 MiB';

  objectSizes: Array<string> = [
    '4 KiB',
    '8 KiB',
    '16 KiB',
    '32 KiB',
    '64 KiB',
    '128 KiB',
    '256 KiB',
    '512 KiB',
    '1 MiB',
    '2 MiB',
    '4 MiB',
    '8 MiB',
    '16 MiB',
    '32 MiB'
  ];
  action: string;
  resource: string;

  constructor(
    private authStorageService: AuthStorageService,
    private route: ActivatedRoute,
    private router: Router,
    private poolService: PoolService,
    private rbdService: RbdService,
    private formatter: FormatterService,
    private taskWrapper: TaskWrapperService,
    private dimlessBinaryPipe: DimlessBinaryPipe,
    private i18n: I18n,
    public actionLabels: ActionLabelsI18n
  ) {
    this.poolPermission = this.authStorageService.getPermissions().pool;
    this.resource = this.i18n('RBD');
    this.features = {
      'deep-flatten': {
        desc: this.i18n('Deep flatten'),
        requires: null,
        allowEnable: false,
        allowDisable: true
      },
      layering: {
        desc: this.i18n('Layering'),
        requires: null,
        allowEnable: false,
        allowDisable: false
      },
      'exclusive-lock': {
        desc: this.i18n('Exclusive lock'),
        requires: null,
        allowEnable: true,
        allowDisable: true
      },
      'object-map': {
        desc: this.i18n('Object map (requires exclusive-lock)'),
        requires: 'exclusive-lock',
        allowEnable: true,
        allowDisable: true
      },
      journaling: {
        desc: this.i18n('Journaling (requires exclusive-lock)'),
        requires: 'exclusive-lock',
        allowEnable: true,
        allowDisable: true
      },
      'fast-diff': {
        desc: this.i18n('Fast diff (requires object-map)'),
        requires: 'object-map',
        allowEnable: true,
        allowDisable: true
      }
    };
    this.createForm();
    for (const key of Object.keys(this.features)) {
      const listItem = this.features[key];
      listItem.key = key;
      this.featuresList.push(listItem);
    }
  }

  createForm() {
    this.deepFlattenFormControl = new FormControl(false);
    this.layeringFormControl = new FormControl(false);
    this.exclusiveLockFormControl = new FormControl(false);
    this.objectMapFormControl = new FormControl({ value: false, disabled: true });
    this.journalingFormControl = new FormControl({ value: false, disabled: true });
    this.fastDiffFormControl = new FormControl({ value: false, disabled: true });
    this.featuresFormGroups = new CdFormGroup({
      'deep-flatten': this.deepFlattenFormControl,
      layering: this.layeringFormControl,
      'exclusive-lock': this.exclusiveLockFormControl,
      'object-map': this.objectMapFormControl,
      journaling: this.journalingFormControl,
      'fast-diff': this.fastDiffFormControl
    });
    this.rbdForm = new CdFormGroup(
      {
        parent: new FormControl(''),
        name: new FormControl('', {
          validators: [Validators.required, Validators.pattern(/^[^@/]+?$/)]
        }),
        pool: new FormControl(null, {
          validators: [Validators.required]
        }),
        useDataPool: new FormControl(false),
        dataPool: new FormControl(null),
        size: new FormControl(null, {
          updateOn: 'blur'
        }),
        obj_size: new FormControl(this.defaultObjectSize),
        features: this.featuresFormGroups,
        stripingUnit: new FormControl(null),
        stripingCount: new FormControl(null, {
          updateOn: 'blur'
        })
      },
      this.validateRbdForm(this.formatter)
    );
  }

  disableForEdit() {
    this.rbdForm.get('parent').disable();
    this.rbdForm.get('pool').disable();
    this.rbdForm.get('useDataPool').disable();
    this.rbdForm.get('dataPool').disable();
    this.rbdForm.get('obj_size').disable();
    this.rbdForm.get('stripingUnit').disable();
    this.rbdForm.get('stripingCount').disable();
  }

  disableForClone() {
    this.rbdForm.get('parent').disable();
    this.rbdForm.get('size').disable();
  }

  disableForCopy() {
    this.rbdForm.get('parent').disable();
    this.rbdForm.get('size').disable();
  }

  ngOnInit() {
    if (this.router.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')) {
      this.mode = this.rbdFormMode.cloning;
      this.disableForClone();
      this.action = this.actionLabels.CLONE;
    } else if (this.router.url.startsWith('/block/rbd/copy')) {
      this.mode = this.rbdFormMode.copying;
      this.action = this.actionLabels.COPY;
      this.disableForCopy();
    } else {
      this.action = this.actionLabels.CREATE;
    }
    if (
      this.mode === this.rbdFormMode.editing ||
      this.mode === this.rbdFormMode.cloning ||
      this.mode === this.rbdFormMode.copying
    ) {
      this.route.params.subscribe((params: { pool: string; name: string; snap: string }) => {
        const poolName = decodeURIComponent(params.pool);
        const rbdName = decodeURIComponent(params.name);
        if (params.snap) {
          this.snapName = decodeURIComponent(params.snap);
        }
        this.rbdService.get(poolName, rbdName).subscribe((resp: RbdFormResponseModel) => {
          this.setResponse(resp, this.snapName);
        });
      });
    } else {
      this.rbdService.defaultFeatures().subscribe((defaultFeatures: Array<string>) => {
        this.setFeatures(defaultFeatures);
      });
    }
    if (this.mode !== this.rbdFormMode.editing && this.poolPermission.read) {
      this.poolService
        .list(['pool_name', 'type', 'flags_names', 'application_metadata'])
        .then((resp) => {
          const pools = [];
          const dataPools = [];
          for (const pool of resp) {
            if (_.indexOf(pool.application_metadata, 'rbd') !== -1) {
              if (!pool.pool_name.includes('/')) {
                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);
          }
        });
    }
    this.deepFlattenFormControl.valueChanges.subscribe((value) => {
      this.watchDataFeatures('deep-flatten', value);
    });
    this.layeringFormControl.valueChanges.subscribe((value) => {
      this.watchDataFeatures('layering', value);
    });
    this.exclusiveLockFormControl.valueChanges.subscribe((value) => {
      this.watchDataFeatures('exclusive-lock', value);
    });
    this.objectMapFormControl.valueChanges.subscribe((value) => {
      this.watchDataFeatures('object-map', value);
    });
    this.journalingFormControl.valueChanges.subscribe((value) => {
      this.watchDataFeatures('journaling', value);
    });
    this.fastDiffFormControl.valueChanges.subscribe((value) => {
      this.watchDataFeatures('fast-diff', value);
    });
  }

  onPoolChange(selectedPoolName) {
    const newDataPools = 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;
  }

  onUseDataPoolChange() {
    if (!this.rbdForm.getValue('useDataPool')) {
      this.rbdForm.get('dataPool').setValue(null);
      this.onDataPoolChange(null);
    }
  }

  onDataPoolChange(selectedDataPoolName) {
    const newPools = this.allPools.filter((pool: any) => {
      return pool.pool_name !== selectedDataPoolName;
    });
    if (this.rbdForm.getValue('pool') === selectedDataPoolName) {
      this.rbdForm.get('pool').setValue(null);
    }
    this.pools = newPools;
  }

  validateRbdForm(formatter: FormatterService): ValidatorFn {
    return (formGroup: CdFormGroup) => {
      // Data Pool
      const useDataPoolControl = formGroup.get('useDataPool');
      const dataPoolControl = formGroup.get('dataPool');
      let dataPoolControlErrors = null;
      if (useDataPoolControl.value && dataPoolControl.value == null) {
        dataPoolControlErrors = { required: true };
      }
      dataPoolControl.setErrors(dataPoolControlErrors);
      // Size
      const sizeControl = formGroup.get('size');
      const objectSizeControl = formGroup.get('obj_size');
      const objectSizeInBytes = formatter.toBytes(
        objectSizeControl.value != null ? objectSizeControl.value : this.defaultObjectSize
      );
      const stripingCountControl = formGroup.get('stripingCount');
      const stripingCount = stripingCountControl.value != null ? stripingCountControl.value : 1;
      let sizeControlErrors = null;
      if (sizeControl.value === null) {
        sizeControlErrors = { required: true };
      } else {
        const sizeInBytes = formatter.toBytes(sizeControl.value);
        if (stripingCount * objectSizeInBytes > sizeInBytes) {
          sizeControlErrors = { invalidSizeObject: true };
        }
      }
      sizeControl.setErrors(sizeControlErrors);
      // Striping Unit
      const stripingUnitControl = formGroup.get('stripingUnit');
      let stripingUnitControlErrors = null;
      if (stripingUnitControl.value === null && stripingCountControl.value !== null) {
        stripingUnitControlErrors = { required: true };
      } else if (stripingUnitControl.value !== null) {
        const stripingUnitInBytes = formatter.toBytes(stripingUnitControl.value);
        if (stripingUnitInBytes > objectSizeInBytes) {
          stripingUnitControlErrors = { invalidStripingUnit: true };
        }
      }
      stripingUnitControl.setErrors(stripingUnitControlErrors);
      // Striping Count
      let stripingCountControlErrors = null;
      if (stripingCountControl.value === null && stripingUnitControl.value !== null) {
        stripingCountControlErrors = { required: true };
      } else if (stripingCount < 1) {
        stripingCountControlErrors = { min: true };
      }
      stripingCountControl.setErrors(stripingCountControlErrors);
      return null;
    };
  }

  deepBoxCheck(key, checked) {
    _.forIn(this.features, (details, feature) => {
      if (details.requires === key) {
        if (checked) {
          this.rbdForm.get(feature).enable();
        } else {
          this.rbdForm.get(feature).disable();
          this.rbdForm.get(feature).setValue(checked);
          this.watchDataFeatures(feature, checked);
          this.deepBoxCheck(feature, checked);
        }
      }
      if (this.mode === this.rbdFormMode.editing && this.rbdForm.get(feature).enabled) {
        if (this.response.features_name.indexOf(feature) !== -1 && !details.allowDisable) {
          this.rbdForm.get(feature).disable();
        } else if (this.response.features_name.indexOf(feature) === -1 && !details.allowEnable) {
          this.rbdForm.get(feature).disable();
        }
      }
    });
  }

  featureFormUpdate(key, checked) {
    if (checked) {
      const required = this.features[key].requires;
      if (required && !this.rbdForm.getValue(required)) {
        this.rbdForm.get(key).setValue(false);
        return;
      }
    }
    this.deepBoxCheck(key, checked);
  }

  watchDataFeatures(key, checked) {
    this.featureFormUpdate(key, checked);
  }

  setFeatures(features: Array<string>) {
    const featuresControl = this.rbdForm.get('features');
    _.forIn(this.features, (feature) => {
      if (features.indexOf(feature.key) !== -1) {
        featuresControl.get(feature.key).setValue(true);
      }
      this.watchDataFeatures(feature.key, featuresControl.get(feature.key).value);
    });
  }

  setResponse(response: RbdFormResponseModel, snapName: string) {
    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) {
      if (snapName) {
        this.rbdForm.get('parent').setValue(`${response.pool_name}/${response.name}@${snapName}`);
      } else {
        this.rbdForm.get('parent').setValue(`${response.pool_name}/${response.name}`);
      }
    } else if (response.parent) {
      const parent = response.parent;
      this.rbdForm
        .get('parent')
        .setValue(`${parent.pool_name}/${parent.image_name}@${parent.snap_name}`);
    }
    if (this.mode === this.rbdFormMode.editing) {
      this.rbdForm.get('name').setValue(response.name);
    }
    this.rbdForm.get('pool').setValue(response.pool_name);
    if (response.data_pool) {
      this.rbdForm.get('useDataPool').setValue(true);
      this.rbdForm.get('dataPool').setValue(response.data_pool);
    }
    this.rbdForm.get('size').setValue(this.dimlessBinaryPipe.transform(response.size));
    this.rbdForm.get('obj_size').setValue(this.dimlessBinaryPipe.transform(response.obj_size));
    this.setFeatures(response.features_name);
    this.rbdForm
      .get('stripingUnit')
      .setValue(this.dimlessBinaryPipe.transform(response.stripe_unit));
    this.rbdForm.get('stripingCount').setValue(response.stripe_count);

    /* Configuration */
    this.initializeConfigData.emit({
      initialData: this.response.configuration,
      sourceType: RbdConfigurationSourceField.image
    });
  }

  createRequest() {
    const request = new RbdFormCreateRequestModel();
    request.pool_name = this.rbdForm.getValue('pool');
    request.name = this.rbdForm.getValue('name');
    request.size = this.formatter.toBytes(this.rbdForm.getValue('size'));
    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 */
    request.configuration = this.getDirtyConfigurationValues();

    return request;
  }

  createAction(): Observable<any> {
    const request = this.createRequest();
    return this.taskWrapper.wrapTaskAroundCall({
      task: new FinishedTask('rbd/create', {
        pool_name: request.pool_name,
        image_name: request.name
      }),
      call: this.rbdService.create(request)
    });
  }

  editRequest() {
    const request = new RbdFormEditRequestModel();
    request.name = this.rbdForm.getValue('name');
    request.size = this.formatter.toBytes(this.rbdForm.getValue('size'));
    _.forIn(this.features, (feature) => {
      if (this.rbdForm.getValue(feature.key)) {
        request.features.push(feature.key);
      }
    });

    request.configuration = this.getDirtyConfigurationValues();

    return request;
  }

  cloneRequest(): RbdFormCloneRequestModel {
    const request = new RbdFormCloneRequestModel();
    request.child_pool_name = this.rbdForm.getValue('pool');
    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 */
    request.configuration = this.getDirtyConfigurationValues(
      true,
      RbdConfigurationSourceField.image
    );

    return request;
  }

  editAction(): Observable<any> {
    return this.taskWrapper.wrapTaskAroundCall({
      task: new FinishedTask('rbd/edit', {
        pool_name: this.response.pool_name,
        image_name: this.response.name
      }),
      call: this.rbdService.update(this.response.pool_name, this.response.name, this.editRequest())
    });
  }

  cloneAction(): Observable<any> {
    const request = this.cloneRequest();
    return this.taskWrapper.wrapTaskAroundCall({
      task: new FinishedTask('rbd/clone', {
        parent_pool_name: this.response.pool_name,
        parent_image_name: this.response.name,
        parent_snap_name: this.snapName,
        child_pool_name: request.child_pool_name,
        child_image_name: request.child_image_name
      }),
      call: this.rbdService.cloneSnapshot(
        this.response.pool_name,
        this.response.name,
        this.snapName,
        request
      )
    });
  }

  copyRequest(): RbdFormCopyRequestModel {
    const request = new RbdFormCopyRequestModel();
    if (this.snapName) {
      request.snapshot_name = this.snapName;
    }
    request.dest_pool_name = this.rbdForm.getValue('pool');
    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 */
    request.configuration = this.getDirtyConfigurationValues(
      true,
      RbdConfigurationSourceField.image
    );

    return request;
  }

  copyAction(): Observable<any> {
    const request = this.copyRequest();

    return this.taskWrapper.wrapTaskAroundCall({
      task: new FinishedTask('rbd/copy', {
        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
      }),
      call: this.rbdService.copy(this.response.pool_name, this.response.name, request)
    });
  }

  submit() {
    let action: Observable<any>;

    if (this.mode === this.rbdFormMode.editing) {
      action = this.editAction();
    } else if (this.mode === this.rbdFormMode.cloning) {
      action = this.cloneAction();
    } else if (this.mode === this.rbdFormMode.copying) {
      action = this.copyAction();
    } else {
      action = this.createAction();
    }

    action.subscribe(
      undefined,
      () => this.rbdForm.setErrors({ cdSubmitButton: true }),
      () => this.router.navigate(['/block/rbd'])
    );
  }
}
<div class="col-sm-12 col-lg-6">
  <form name="rbdForm"
        class="form-horizontal"
        #formDir="ngForm"
        [formGroup]="rbdForm"
        novalidate>
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 i18n="form title|Example: Create Pool@@formTitle"
            class="panel-title">{{ action | titlecase }} {{ resource | upperFirst }}</h3>
      </div>
      <div class="panel-body">

        <!-- Parent -->
        <div class="form-group"
             *ngIf="rbdForm.getValue('parent')">
          <label i18n
                 class="control-label col-sm-3"
                 for="name">{{ action | titlecase }} from</label>
          <div class="col-sm-9">
            <input class="form-control"
                   type="text"
                   id="parent"
                   name="parent"
                   formControlName="parent">
            <hr>
          </div>
        </div>

        <!-- Name -->
        <div class="form-group"
             [ngClass]="{'has-error': rbdForm.showError('name', formDir)}">
          <label class="control-label col-sm-3"
                 for="name">
            <ng-container i18n>Name</ng-container>
            <span class="required"></span>
          </label>
          <div class="col-sm-9">
            <input class="form-control"
                   type="text"
                   placeholder="Name..."
                   id="name"
                   name="name"
                   formControlName="name"
                   autofocus>
            <span class="help-block"
                  *ngIf="rbdForm.showError('name', formDir, 'required')">
              <ng-container i18n>This field is required.</ng-container>
            </span>
            <span class="help-block"
                  *ngIf="rbdForm.showError('name', formDir, 'pattern')">
              <ng-container i18n>'/' and '@' are not allowed.</ng-container>
            </span>
          </div>
        </div>

        <!-- Pool -->
        <div class="form-group"
             [ngClass]="{'has-error': rbdForm.showError('pool', formDir)}"
             (change)="onPoolChange($event.target.value)">
          <label class="control-label col-sm-3"
                 for="pool">
            Pool
            <span class="required"
                  *ngIf="mode !== 'editing'"></span>
          </label>
          <div class="col-sm-9">
            <input class="form-control"
                   type="text"
                   placeholder="Pool name..."
                   id="pool"
                   name="pool"
                   formControlName="pool"
                   *ngIf="mode === 'editing' || !poolPermission.read">
            <select id="pool"
                    name="pool"
                    class="form-control"
                    formControlName="pool"
                    *ngIf="mode !== 'editing' && poolPermission.read">
              <option *ngIf="pools === null"
                      [ngValue]="null"
                      i18n>Loading...</option>
              <option *ngIf="pools !== null && pools.length === 0"
                      [ngValue]="null"
                      i18n>-- No rbd pools available --</option>
              <option *ngIf="pools !== null && pools.length > 0"
                      [ngValue]="null"
                      i18n>-- Select a pool --</option>
              <option *ngFor="let pool of pools"
                      [value]="pool.pool_name">{{ pool.pool_name }}</option>
            </select>
            <span *ngIf="rbdForm.showError('pool', formDir, 'required')"
                  class="help-block"
                  i18n>This field is required.</span>
          </div>
        </div>

        <!-- Use a dedicated pool -->
        <div class="form-group">
          <div class="col-sm-offset-3 col-sm-9">
            <div class="checkbox checkbox-primary">
              <input type="checkbox"
                     id="useDataPool"
                     name="useDataPool"
                     formControlName="useDataPool"
                     (change)="onUseDataPoolChange()">
              <label i18n
                     for="useDataPool">Use a dedicated data pool</label>
            </div>
          </div>
        </div>

        <!-- Data Pool -->
        <div class="form-group"
             [ngClass]="{'has-error': rbdForm.showError('dataPool', formDir)}"
             *ngIf="rbdForm.getValue('useDataPool')">
          <label class="control-label col-sm-3"
                 for="dataPool">
            <ng-container i18n>Data pool</ng-container>
            <span class="required"
                  *ngIf="mode !== 'editing'"></span>
            <cd-helper i18n-html
                       html="Dedicated pool that stores the object-data of the RBD.">
            </cd-helper>
          </label>
          <div class="col-sm-9">
            <input class="form-control"
                   type="text"
                   placeholder="Data pool name..."
                   id="dataPool"
                   name="dataPool"
                   formControlName="dataPool"
                   *ngIf="mode === 'editing' || !poolPermission.read">
            <select id="dataPool"
                    name="dataPool"
                    class="form-control"
                    formControlName="dataPool"
                    (change)="onDataPoolChange($event.target.value)"
                    *ngIf="mode !== 'editing' && poolPermission.read">
              <option *ngIf="dataPools === null"
                      [ngValue]="null"
                      i18n>Loading...</option>
              <option *ngIf="dataPools !== null && dataPools.length === 0"
                      [ngValue]="null"
                      i18n>-- No data pools available --</option>
              <option *ngIf="dataPools !== null && dataPools.length > 0"
                      [ngValue]="null">-- Select a data pool --
              </option>
              <option *ngFor="let dataPool of dataPools"
                      [value]="dataPool.pool_name">{{ dataPool.pool_name }}</option>
            </select>
            <span class="help-block"
                  *ngIf="rbdForm.showError('dataPool', formDir, 'required')"
                  i18n>This field is required.</span>
          </div>
        </div>

        <!-- Size -->
        <div class="form-group"
             [ngClass]="{'has-error': rbdForm.showError('size', formDir)}">
          <label class="control-label col-sm-3"
                 for="size">
            <ng-container i18n>Size</ng-container>
            <span class="required"></span>
          </label>
          <div class="col-sm-9">
            <input id="size"
                   name="size"
                   class="form-control"
                   type="text"
                   formControlName="size"
                   i18n-placeholder
                   placeholder="e.g., 10GiB"
                   defaultUnit="GiB"
                   cdDimlessBinary>
            <span class="help-block"
                  *ngIf="rbdForm.showError('size', formDir, 'required')"
                  i18n>This field is required.</span>
            <span class="help-block"
                  *ngIf="rbdForm.showError('size', formDir, 'invalidSizeObject')"
                  i18n>You have to increase the size.</span>
          </div>
        </div>

        <!-- Features -->
        <div class="form-group"
             [ngClass]="{'has-error': (formDir.submitted || featuresFormGroups.dirty) && featuresFormGroups.invalid}"
             formGroupName="features">
          <label i18n
                 class="col-sm-3 control-label"
                 for="features">Features</label>
          <div class="col-sm-9">
            <div class="checkbox checkbox-primary"
                 *ngFor="let feature of featuresList">
              <input type="checkbox"
                     id="{{ feature.key }}"
                     name="{{ feature.key }}"
                     formControlName="{{ feature.key }}">
              <label for="{{ feature.key }}">{{ feature.desc }}</label>
              <cd-helper *ngIf="feature.helperHtml"
                         html="{{ feature.helperHtml }}">
              </cd-helper>
            </div>
          </div>
        </div>

        <!-- Advanced -->
        <div class="row">
          <div class="col-sm-12">
            <a class="pull-right margin-right-md"
               (click)="advancedEnabled = true"
               *ngIf="!advancedEnabled"
               i18n>Advanced...</a>
          </div>
        </div>
        <div [hidden]="!advancedEnabled">

          <h2 i18n
              class="page-header">Advanced</h2>

          <div class="section">
            <h3 class="page-header" i18n>Striping</h3>

            <!-- Object Size -->
            <div class="form-group"
                 [ngClass]="{'has-error': rbdForm.showError('obj_size', formDir)}">
              <label i18n
                     class="control-label col-sm-3"
                     for="size">Object size</label>
              <div class="col-sm-9">
                <select id="obj_size"
                        name="obj_size"
                        class="form-control"
                        formControlName="obj_size">
                  <option *ngFor="let objectSize of objectSizes"
                          [value]="objectSize">{{ objectSize }}</option>
                </select>
              </div>
            </div>

            <!-- Stripe Unit -->
            <div class="form-group"
                 [ngClass]="{'has-error': rbdForm.showError('stripingUnit', formDir)}">
              <label class="control-label col-sm-3"
                     for="stripingUnit">
                <span i18n>Stripe unit</span>
                <span class="required"
                      *ngIf="rbdForm.getValue('stripingCount')">
              </span>
              </label>
              <div class="col-sm-9">
                <select id="stripingUnit"
                        name="stripingUnit"
                        class="form-control"
                        formControlName="stripingUnit">
                  <option i18n
                          [ngValue]="null">-- Select stripe unit --</option>
                  <option *ngFor="let objectSize of objectSizes"
                          [value]="objectSize">{{ objectSize }}</option>
                </select>
                <span class="help-block"
                      *ngIf="rbdForm.showError('stripingUnit', formDir, 'required')"
                      i18n>This field is required because stripe count is defined!</span>
                <span class="help-block"
                      *ngIf="rbdForm.showError('stripingUnit', formDir, 'invalidStripingUnit')"
                      i18n>Stripe unit is greater than object size.</span>
              </div>
            </div>

            <!-- Stripe Count -->
            <div class="form-group"
                 [ngClass]="{'has-error': rbdForm.showError('stripingCount', formDir)}">
              <label class="control-label col-sm-3"
                     for="stripingCount">
                <span i18n>Stripe count</span>
                <span class="required"
                      *ngIf="rbdForm.getValue('stripingUnit')">
              </span>
              </label>
              <div class="col-sm-9">
                <input id="stripingCount"
                       name="stripingCount"
                       formControlName="stripingCount"
                       class="form-control"
                       type="number">
                <span class="help-block"
                      *ngIf="rbdForm.showError('stripingCount', formDir, 'required')"
                      i18n>This field is required because stripe unit is defined!</span>
                <span class="help-block"
                      *ngIf="rbdForm.showError('stripingCount', formDir, 'min')"
                      i18n>Stripe count must be greater than 0.</span>
              </div>
            </div>
          </div>

          <div class="section">
            <cd-rbd-configuration-form [form]="rbdForm"
                                       [initializeData]="initializeConfigData"
                                       (changes)="getDirtyConfigurationValues = $event"></cd-rbd-configuration-form>
          </div>

        </div>

      </div>
      <div class="panel-footer">
        <div class="button-group text-right">
          <cd-submit-button
            [form]="formDir"
            (submitAction)="submit()"
            i18n="form action button|Example: Create Pool@@formActionButton"
            type="button">{{ action | titlecase }} {{ resource | upperFirst }}</cd-submit-button>
          <cd-back-button></cd-back-button>
        </div>
      </div>
    </div>
  </form>
</div>

./rbd-form.component.scss

Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""