]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/create-cluster/create-cluster.component.ts
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / create-cluster / create-cluster.component.ts
1 import {
2 Component,
3 EventEmitter,
4 OnDestroy,
5 OnInit,
6 Output,
7 TemplateRef,
8 ViewChild
9 } from '@angular/core';
10 import { Router } from '@angular/router';
11
12 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
13 import _ from 'lodash';
14 import { forkJoin, Subscription } from 'rxjs';
15 import { finalize } from 'rxjs/operators';
16
17 import { ClusterService } from '~/app/shared/api/cluster.service';
18 import { HostService } from '~/app/shared/api/host.service';
19 import { OsdService } from '~/app/shared/api/osd.service';
20 import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component';
21 import { ActionLabelsI18n, AppConstants, URLVerbs } from '~/app/shared/constants/app.constants';
22 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
23 import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
24 import { FinishedTask } from '~/app/shared/models/finished-task';
25 import { DeploymentOptions } from '~/app/shared/models/osd-deployment-options';
26 import { Permissions } from '~/app/shared/models/permissions';
27 import { WizardStepModel } from '~/app/shared/models/wizard-steps';
28 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
29 import { ModalService } from '~/app/shared/services/modal.service';
30 import { NotificationService } from '~/app/shared/services/notification.service';
31 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
32 import { WizardStepsService } from '~/app/shared/services/wizard-steps.service';
33 import { DriveGroup } from '../osd/osd-form/drive-group.model';
34
35 @Component({
36 selector: 'cd-create-cluster',
37 templateUrl: './create-cluster.component.html',
38 styleUrls: ['./create-cluster.component.scss']
39 })
40 export class CreateClusterComponent implements OnInit, OnDestroy {
41 @ViewChild('skipConfirmTpl', { static: true })
42 skipConfirmTpl: TemplateRef<any>;
43 currentStep: WizardStepModel;
44 currentStepSub: Subscription;
45 permissions: Permissions;
46 projectConstants: typeof AppConstants = AppConstants;
47 stepTitles = ['Add Hosts', 'Create OSDs', 'Create Services', 'Review'];
48 startClusterCreation = false;
49 observables: any = [];
50 modalRef: NgbModalRef;
51 driveGroup = new DriveGroup();
52 driveGroups: Object[] = [];
53 deploymentOption: DeploymentOptions;
54 selectedOption = {};
55 simpleDeployment = true;
56 stepsToSkip: { [steps: string]: boolean } = {};
57
58 @Output()
59 submitAction = new EventEmitter();
60
61 constructor(
62 private authStorageService: AuthStorageService,
63 private wizardStepsService: WizardStepsService,
64 private router: Router,
65 private hostService: HostService,
66 private notificationService: NotificationService,
67 private actionLabels: ActionLabelsI18n,
68 private clusterService: ClusterService,
69 private modalService: ModalService,
70 private taskWrapper: TaskWrapperService,
71 private osdService: OsdService
72 ) {
73 this.permissions = this.authStorageService.getPermissions();
74 this.currentStepSub = this.wizardStepsService
75 .getCurrentStep()
76 .subscribe((step: WizardStepModel) => {
77 this.currentStep = step;
78 });
79 this.currentStep.stepIndex = 1;
80 }
81
82 ngOnInit(): void {
83 this.osdService.getDeploymentOptions().subscribe((options) => {
84 this.deploymentOption = options;
85 this.selectedOption = { option: options.recommended_option, encrypted: false };
86 });
87
88 this.stepTitles.forEach((stepTitle) => {
89 this.stepsToSkip[stepTitle] = false;
90 });
91 }
92
93 createCluster() {
94 this.startClusterCreation = true;
95 }
96
97 skipClusterCreation() {
98 const modalVariables = {
99 titleText: $localize`Warning`,
100 buttonText: $localize`Continue`,
101 warning: true,
102 bodyTpl: this.skipConfirmTpl,
103 showSubmit: true,
104 onSubmit: () => {
105 this.clusterService.updateStatus('POST_INSTALLED').subscribe({
106 error: () => this.modalRef.close(),
107 complete: () => {
108 this.notificationService.show(
109 NotificationType.info,
110 $localize`Cluster expansion skipped by user`
111 );
112 this.router.navigate(['/dashboard']);
113 this.modalRef.close();
114 }
115 });
116 }
117 };
118 this.modalRef = this.modalService.show(ConfirmationModalComponent, modalVariables);
119 }
120
121 onSubmit() {
122 if (!this.stepsToSkip['Add Hosts']) {
123 const hostContext = new CdTableFetchDataContext(() => undefined);
124 this.hostService.list(hostContext.toParams(), 'false').subscribe((hosts) => {
125 hosts.forEach((host) => {
126 const index = host['labels'].indexOf('_no_schedule', 0);
127 if (index > -1) {
128 host['labels'].splice(index, 1);
129 this.observables.push(this.hostService.update(host['hostname'], true, host['labels']));
130 }
131 });
132 forkJoin(this.observables)
133 .pipe(
134 finalize(() =>
135 this.clusterService.updateStatus('POST_INSTALLED').subscribe(() => {
136 this.notificationService.show(
137 NotificationType.success,
138 $localize`Cluster expansion was successful`
139 );
140 this.router.navigate(['/dashboard']);
141 })
142 )
143 )
144 .subscribe({
145 error: (error) => error.preventDefault()
146 });
147 });
148 }
149
150 if (!this.stepsToSkip['Create OSDs']) {
151 if (this.driveGroup) {
152 const user = this.authStorageService.getUsername();
153 this.driveGroup.setName(`dashboard-${user}-${_.now()}`);
154 this.driveGroups.push(this.driveGroup.spec);
155 }
156
157 if (this.simpleDeployment) {
158 const title = this.deploymentOption?.options[this.selectedOption['option']].title;
159 const trackingId = $localize`${title} deployment`;
160 this.taskWrapper
161 .wrapTaskAroundCall({
162 task: new FinishedTask('osd/' + URLVerbs.CREATE, {
163 tracking_id: trackingId
164 }),
165 call: this.osdService.create([this.selectedOption], trackingId, 'predefined')
166 })
167 .subscribe({
168 error: (error) => error.preventDefault(),
169 complete: () => {
170 this.submitAction.emit();
171 }
172 });
173 } else {
174 if (this.osdService.osdDevices['totalDevices'] > 0) {
175 this.driveGroup.setFeature('encrypted', this.selectedOption['encrypted']);
176 const trackingId = _.join(_.map(this.driveGroups, 'service_id'), ', ');
177 this.taskWrapper
178 .wrapTaskAroundCall({
179 task: new FinishedTask('osd/' + URLVerbs.CREATE, {
180 tracking_id: trackingId
181 }),
182 call: this.osdService.create(this.driveGroups, trackingId)
183 })
184 .subscribe({
185 error: (error) => error.preventDefault(),
186 complete: () => {
187 this.submitAction.emit();
188 this.osdService.osdDevices = [];
189 }
190 });
191 }
192 }
193 }
194 }
195
196 setDriveGroup(driveGroup: DriveGroup) {
197 this.driveGroup = driveGroup;
198 }
199
200 setDeploymentOptions(option: object) {
201 this.selectedOption = option;
202 }
203
204 setDeploymentMode(mode: boolean) {
205 this.simpleDeployment = mode;
206 }
207
208 onNextStep() {
209 if (!this.wizardStepsService.isLastStep()) {
210 this.wizardStepsService.getCurrentStep().subscribe((step: WizardStepModel) => {
211 this.currentStep = step;
212 });
213 this.wizardStepsService.moveToNextStep();
214 } else {
215 this.onSubmit();
216 }
217 }
218
219 onPreviousStep() {
220 if (!this.wizardStepsService.isFirstStep()) {
221 this.wizardStepsService.moveToPreviousStep();
222 } else {
223 this.router.navigate(['/dashboard']);
224 }
225 }
226
227 onSkip() {
228 const stepTitle = this.stepTitles[this.currentStep.stepIndex - 1];
229 this.stepsToSkip[stepTitle] = true;
230 this.onNextStep();
231 }
232
233 showSubmitButtonLabel() {
234 return !this.wizardStepsService.isLastStep()
235 ? this.actionLabels.NEXT
236 : $localize`Expand Cluster`;
237 }
238
239 showCancelButtonLabel() {
240 return !this.wizardStepsService.isFirstStep()
241 ? this.actionLabels.BACK
242 : this.actionLabels.CANCEL;
243 }
244
245 ngOnDestroy(): void {
246 this.currentStepSub.unsubscribe();
247 }
248 }