]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/blob
d2a136f31198128fcbf16db02ea4c29063e0f46d
[ceph.git] /
1 import { Component, OnInit } from '@angular/core';
2 import { UntypedFormControl, Validators } from '@angular/forms';
3 import { ActivatedRoute, Router } from '@angular/router';
4 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
5 import {
6   NamespaceCreateRequest,
7   NamespaceUpdateRequest,
8   NvmeofService
9 } from '~/app/shared/api/nvmeof.service';
10 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
11 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
12 import { FinishedTask } from '~/app/shared/models/finished-task';
13 import { NvmeofSubsystemNamespace } from '~/app/shared/models/nvmeof';
14 import { Permission } from '~/app/shared/models/permissions';
15 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
16 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
17 import { Pool } from '../../pool/pool';
18 import { PoolService } from '~/app/shared/api/pool.service';
19 import { RbdService } from '~/app/shared/api/rbd.service';
20 import { FormatterService } from '~/app/shared/services/formatter.service';
21 import { forkJoin, Observable } from 'rxjs';
22 import { CdValidators } from '~/app/shared/forms/cd-validators';
23 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
24 import { HttpResponse } from '@angular/common/http';
25
26 @Component({
27   selector: 'cd-nvmeof-namespaces-form',
28   templateUrl: './nvmeof-namespaces-form.component.html',
29   styleUrls: ['./nvmeof-namespaces-form.component.scss']
30 })
31 export class NvmeofNamespacesFormComponent implements OnInit {
32   action: string;
33   permission: Permission;
34   poolPermission: Permission;
35   resource: string;
36   pageURL: string;
37   edit: boolean = false;
38   nsForm: CdFormGroup;
39   subsystemNQN: string;
40   rbdPools: Array<Pool> = null;
41   units: Array<string> = ['MiB', 'GiB', 'TiB'];
42   nsid: string;
43   currentBytes: number;
44   invalidSizeError: boolean;
45   group: string;
46   MAX_NAMESPACE_CREATE: number = 5;
47   MIN_NAMESPACE_CREATE: number = 1;
48   requiredInvalidText: string = $localize`This field is required`;
49   nsCountInvalidText: string = $localize`The namespace count should be between 1 and 5`;
50
51   constructor(
52     public actionLabels: ActionLabelsI18n,
53     private authStorageService: AuthStorageService,
54     private taskWrapperService: TaskWrapperService,
55     private nvmeofService: NvmeofService,
56     private poolService: PoolService,
57     private rbdService: RbdService,
58     private router: Router,
59     private route: ActivatedRoute,
60     public activeModal: NgbActiveModal,
61     public formatterService: FormatterService,
62     public dimlessBinaryPipe: DimlessBinaryPipe
63   ) {
64     this.permission = this.authStorageService.getPermissions().nvmeof;
65     this.poolPermission = this.authStorageService.getPermissions().pool;
66     this.resource = $localize`Namespace`;
67     this.pageURL = 'block/nvmeof/subsystems';
68   }
69
70   init() {
71     this.route.queryParams.subscribe((params) => {
72       this.group = params?.['group'];
73     });
74     this.createForm();
75     this.action = this.actionLabels.CREATE;
76     this.route.params.subscribe((params: { subsystem_nqn: string; nsid: string }) => {
77       this.subsystemNQN = params.subsystem_nqn;
78       this.nsid = params?.nsid;
79     });
80   }
81
82   initForEdit() {
83     this.edit = true;
84     this.action = this.actionLabels.EDIT;
85     this.nvmeofService
86       .getNamespace(this.subsystemNQN, this.nsid, this.group)
87       .subscribe((res: NvmeofSubsystemNamespace) => {
88         const convertedSize = this.dimlessBinaryPipe.transform(res.rbd_image_size).split(' ');
89         this.currentBytes = res.rbd_image_size;
90         this.nsForm.get('pool').setValue(res.rbd_pool_name);
91         this.nsForm.get('unit').setValue(convertedSize[1]);
92         this.nsForm.get('image_size').setValue(convertedSize[0]);
93         this.nsForm.get('image_size').addValidators(Validators.required);
94         this.nsForm.get('pool').disable();
95       });
96   }
97
98   initForCreate() {
99     this.poolService.getList().subscribe((resp: Pool[]) => {
100       this.rbdPools = resp.filter(this.rbdService.isRBDPool);
101       if (this.rbdPools?.length) {
102         this.nsForm.get('pool').setValue(this.rbdPools[0].pool_name);
103       }
104     });
105   }
106
107   ngOnInit() {
108     this.init();
109     if (this.router.url.includes('subsystems/(modal:edit')) {
110       this.initForEdit();
111     } else {
112       this.initForCreate();
113     }
114   }
115
116   createForm() {
117     this.nsForm = new CdFormGroup({
118       pool: new UntypedFormControl(null, {
119         validators: [Validators.required]
120       }),
121       image_size: new UntypedFormControl(1, [CdValidators.number(false), Validators.min(1)]),
122       unit: new UntypedFormControl(this.units[1]),
123       nsCount: new UntypedFormControl(this.MAX_NAMESPACE_CREATE, [
124         Validators.required,
125         Validators.max(this.MAX_NAMESPACE_CREATE),
126         Validators.min(this.MIN_NAMESPACE_CREATE)
127       ])
128     });
129   }
130
131   buildUpdateRequest(rbdImageSize: number): Observable<HttpResponse<Object>> {
132     const request: NamespaceUpdateRequest = {
133       gw_group: this.group,
134       rbd_image_size: rbdImageSize
135     };
136     return this.nvmeofService.updateNamespace(
137       this.subsystemNQN,
138       this.nsid,
139       request as NamespaceUpdateRequest
140     );
141   }
142
143   randomString() {
144     return Math.random().toString(36).substring(2);
145   }
146
147   buildCreateRequest(rbdImageSize: number, nsCount: number): Observable<HttpResponse<Object>>[] {
148     const pool = this.nsForm.getValue('pool');
149     const requests: Observable<HttpResponse<Object>>[] = [];
150
151     for (let i = 1; i <= nsCount; i++) {
152       const request: NamespaceCreateRequest = {
153         gw_group: this.group,
154         rbd_image_name: `nvme_${pool}_${this.group}_${this.randomString()}`,
155         rbd_pool: pool
156       };
157       if (rbdImageSize) {
158         request['rbd_image_size'] = rbdImageSize;
159       }
160       requests.push(this.nvmeofService.createNamespace(this.subsystemNQN, request));
161     }
162
163     return requests;
164   }
165
166   validateSize() {
167     const unit = this.nsForm.getValue('unit');
168     const image_size = this.nsForm.getValue('image_size');
169     if (image_size && unit) {
170       const bytes = this.formatterService.toBytes(image_size + unit);
171       return bytes <= this.currentBytes;
172     }
173     return null;
174   }
175
176   onSubmit() {
177     if (this.validateSize()) {
178       this.invalidSizeError = true;
179       this.nsForm.setErrors({ cdSubmitButton: true });
180     } else {
181       this.invalidSizeError = false;
182       const component = this;
183       const taskUrl: string = `nvmeof/namespace/${this.edit ? URLVerbs.EDIT : URLVerbs.CREATE}`;
184       const image_size = this.nsForm.getValue('image_size');
185       const nsCount = this.nsForm.getValue('nsCount');
186       let action: Observable<HttpResponse<Object>>;
187       let rbdImageSize: number = null;
188
189       if (image_size) {
190         const image_size_unit = this.nsForm.getValue('unit');
191         const value: number = this.formatterService.toBytes(image_size + image_size_unit);
192         rbdImageSize = value;
193       }
194       if (this.edit) {
195         action = this.taskWrapperService.wrapTaskAroundCall({
196           task: new FinishedTask(taskUrl, {
197             nqn: this.subsystemNQN,
198             nsid: this.nsid
199           }),
200           call: this.buildUpdateRequest(rbdImageSize)
201         });
202       } else {
203         action = this.taskWrapperService.wrapTaskAroundCall({
204           task: new FinishedTask(taskUrl, {
205             nqn: this.subsystemNQN,
206             nsCount
207           }),
208           call: forkJoin(this.buildCreateRequest(rbdImageSize, nsCount))
209         });
210       }
211
212       action.subscribe({
213         error() {
214           component.nsForm.setErrors({ cdSubmitButton: true });
215         },
216         complete: () => {
217           this.router.navigate([this.pageURL, { outlets: { modal: null } }]);
218         }
219       });
220     }
221   }
222 }