-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';
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: (
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;
action: string;
resource: string;
private rbdImage = new ReplaySubject(1);
+ private routerUrl: string;
icons = Icons;
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,
}
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];
};
}
- 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) {
});
}
+ 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
.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
});
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)) {
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> {
request.features.push(feature.key);
}
});
-
request.configuration = this.getDirtyConfigurationValues();
-
return request;
}
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;
}
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;
}
})
)
.subscribe(
- () => {},
+ () => undefined,
() => this.rbdForm.setErrors({ cdSubmitButton: true }),
() => this.router.navigate(['/block/rbd'])
);