]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts
import ceph 16.2.7
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / cluster / osd / osd-form / osd-form.component.ts
1 import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
2 import { FormControl, Validators } from '@angular/forms';
3 import { Router } from '@angular/router';
4
5 import _ from 'lodash';
6
7 import { InventoryDevice } from '~/app/ceph/cluster/inventory/inventory-devices/inventory-device.model';
8 import { HostService } from '~/app/shared/api/host.service';
9 import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
10 import { FormButtonPanelComponent } from '~/app/shared/components/form-button-panel/form-button-panel.component';
11 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
12 import { Icons } from '~/app/shared/enum/icons.enum';
13 import { CdForm } from '~/app/shared/forms/cd-form';
14 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
15 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
16 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
17 import { ModalService } from '~/app/shared/services/modal.service';
18 import { WizardStepsService } from '~/app/shared/services/wizard-steps.service';
19 import { OsdCreationPreviewModalComponent } from '../osd-creation-preview-modal/osd-creation-preview-modal.component';
20 import { DevicesSelectionChangeEvent } from '../osd-devices-selection-groups/devices-selection-change-event.interface';
21 import { DevicesSelectionClearEvent } from '../osd-devices-selection-groups/devices-selection-clear-event.interface';
22 import { OsdDevicesSelectionGroupsComponent } from '../osd-devices-selection-groups/osd-devices-selection-groups.component';
23 import { DriveGroup } from './drive-group.model';
24 import { OsdFeature } from './osd-feature.interface';
25
26 @Component({
27 selector: 'cd-osd-form',
28 templateUrl: './osd-form.component.html',
29 styleUrls: ['./osd-form.component.scss']
30 })
31 export class OsdFormComponent extends CdForm implements OnInit {
32 @ViewChild('dataDeviceSelectionGroups')
33 dataDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent;
34
35 @ViewChild('walDeviceSelectionGroups')
36 walDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent;
37
38 @ViewChild('dbDeviceSelectionGroups')
39 dbDeviceSelectionGroups: OsdDevicesSelectionGroupsComponent;
40
41 @ViewChild('previewButtonPanel')
42 previewButtonPanel: FormButtonPanelComponent;
43
44 @Input()
45 hideTitle = false;
46
47 @Input()
48 hideSubmitBtn = false;
49
50 @Output() emitDriveGroup: EventEmitter<DriveGroup> = new EventEmitter();
51
52 icons = Icons;
53
54 form: CdFormGroup;
55 columns: Array<CdTableColumn> = [];
56
57 allDevices: InventoryDevice[] = [];
58
59 availDevices: InventoryDevice[] = [];
60 dataDeviceFilters: any[] = [];
61 dbDeviceFilters: any[] = [];
62 walDeviceFilters: any[] = [];
63 hostname = '';
64 driveGroup = new DriveGroup();
65
66 action: string;
67 resource: string;
68
69 features: { [key: string]: OsdFeature };
70 featureList: OsdFeature[] = [];
71
72 hasOrchestrator = true;
73
74 constructor(
75 public actionLabels: ActionLabelsI18n,
76 private authStorageService: AuthStorageService,
77 private orchService: OrchestratorService,
78 private hostService: HostService,
79 private router: Router,
80 private modalService: ModalService,
81 public wizardStepService: WizardStepsService
82 ) {
83 super();
84 this.resource = $localize`OSDs`;
85 this.action = this.actionLabels.CREATE;
86 this.features = {
87 encrypted: {
88 key: 'encrypted',
89 desc: $localize`Encryption`
90 }
91 };
92 this.featureList = _.map(this.features, (o, key) => Object.assign(o, { key: key }));
93 this.createForm();
94 }
95
96 ngOnInit() {
97 this.orchService.status().subscribe((status) => {
98 this.hasOrchestrator = status.available;
99 if (status.available) {
100 this.getDataDevices();
101 } else {
102 this.loadingNone();
103 }
104 });
105
106 this.form.get('walSlots').valueChanges.subscribe((value) => this.setSlots('wal', value));
107 this.form.get('dbSlots').valueChanges.subscribe((value) => this.setSlots('db', value));
108 _.each(this.features, (feature) => {
109 this.form
110 .get('features')
111 .get(feature.key)
112 .valueChanges.subscribe((value) => this.featureFormUpdate(feature.key, value));
113 });
114 }
115
116 createForm() {
117 this.form = new CdFormGroup({
118 walSlots: new FormControl(0, {
119 validators: [Validators.min(0)]
120 }),
121 dbSlots: new FormControl(0, {
122 validators: [Validators.min(0)]
123 }),
124 features: new CdFormGroup(
125 this.featureList.reduce((acc: object, e) => {
126 // disable initially because no data devices are selected
127 acc[e.key] = new FormControl({ value: false, disabled: true });
128 return acc;
129 }, {})
130 )
131 });
132 }
133
134 getDataDevices() {
135 this.hostService.inventoryDeviceList().subscribe(
136 (devices: InventoryDevice[]) => {
137 this.allDevices = _.filter(devices, 'available');
138 this.availDevices = [...this.allDevices];
139 this.loadingReady();
140 },
141 () => {
142 this.allDevices = [];
143 this.availDevices = [];
144 this.loadingError();
145 }
146 );
147 }
148
149 setSlots(type: string, slots: number) {
150 if (typeof slots !== 'number') {
151 return;
152 }
153 if (slots >= 0) {
154 this.driveGroup.setSlots(type, slots);
155 }
156 }
157
158 featureFormUpdate(key: string, checked: boolean) {
159 this.driveGroup.setFeature(key, checked);
160 }
161
162 enableFeatures() {
163 this.featureList.forEach((feature) => {
164 this.form.get(feature.key).enable({ emitEvent: false });
165 });
166 }
167
168 disableFeatures() {
169 this.featureList.forEach((feature) => {
170 const control = this.form.get(feature.key);
171 control.disable({ emitEvent: false });
172 control.setValue(false, { emitEvent: false });
173 });
174 }
175
176 onDevicesSelected(event: DevicesSelectionChangeEvent) {
177 this.availDevices = event.dataOut;
178
179 if (event.type === 'data') {
180 // If user selects data devices for a single host, make only remaining devices on
181 // that host as available.
182 const hostnameFilter = _.find(event.filters, { prop: 'hostname' });
183 if (hostnameFilter) {
184 this.hostname = hostnameFilter.value.raw;
185 this.availDevices = event.dataOut.filter((device: InventoryDevice) => {
186 return device.hostname === this.hostname;
187 });
188 this.driveGroup.setHostPattern(this.hostname);
189 } else {
190 this.driveGroup.setHostPattern('*');
191 }
192 this.enableFeatures();
193 }
194 this.driveGroup.setDeviceSelection(event.type, event.filters);
195
196 this.emitDriveGroup.emit(this.driveGroup);
197 }
198
199 onDevicesCleared(event: DevicesSelectionClearEvent) {
200 if (event.type === 'data') {
201 this.availDevices = [...this.allDevices];
202 this.walDeviceSelectionGroups.devices = [];
203 this.dbDeviceSelectionGroups.devices = [];
204 this.disableFeatures();
205 this.driveGroup.reset();
206 this.form.get('walSlots').setValue(0, { emitEvent: false });
207 this.form.get('dbSlots').setValue(0, { emitEvent: false });
208 } else {
209 this.availDevices = [...this.availDevices, ...event.clearedDevices];
210 this.driveGroup.clearDeviceSelection(event.type);
211 const slotControlName = `${event.type}Slots`;
212 this.form.get(slotControlName).setValue(0, { emitEvent: false });
213 }
214 }
215
216 submit() {
217 // use user name and timestamp for drive group name
218 const user = this.authStorageService.getUsername();
219 this.driveGroup.setName(`dashboard-${user}-${_.now()}`);
220 const modalRef = this.modalService.show(OsdCreationPreviewModalComponent, {
221 driveGroups: [this.driveGroup.spec]
222 });
223 modalRef.componentInstance.submitAction.subscribe(() => {
224 this.router.navigate(['/osd']);
225 });
226 this.previewButtonPanel.submitButton.loading = false;
227 }
228 }