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