]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
0ef0449c48126dfd762e57b02b3b3ca5970ef278
[ceph-ci.git] /
1 import {
2   Component,
3   EventEmitter,
4   Input,
5   OnDestroy,
6   OnInit,
7   Output,
8   ViewChild
9 } from '@angular/core';
10
11 import _ from 'lodash';
12 import { Subscription } from 'rxjs';
13
14 import { HostService } from '~/app/shared/api/host.service';
15 import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
16 import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component';
17 import { TableComponent } from '~/app/shared/datatable/table/table.component';
18 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
19 import { Icons } from '~/app/shared/enum/icons.enum';
20 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
21 import { CdTableAction } from '~/app/shared/models/cd-table-action';
22 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
23 import { CdTableColumnFiltersChange } from '~/app/shared/models/cd-table-column-filters-change';
24 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
25 import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum';
26 import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface';
27 import { Permission } from '~/app/shared/models/permissions';
28 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
29 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
30 import { ModalService } from '~/app/shared/services/modal.service';
31 import { NotificationService } from '~/app/shared/services/notification.service';
32 import { InventoryDevice } from './inventory-device.model';
33
34 @Component({
35   selector: 'cd-inventory-devices',
36   templateUrl: './inventory-devices.component.html',
37   styleUrls: ['./inventory-devices.component.scss']
38 })
39 export class InventoryDevicesComponent implements OnInit, OnDestroy {
40   @ViewChild(TableComponent, { static: true })
41   table: TableComponent;
42
43   // Devices
44   @Input() devices: InventoryDevice[] = [];
45
46   @Input() showAvailDeviceOnly = false;
47   // Do not display these columns
48   @Input() hiddenColumns: string[] = [];
49
50   @Input() hostname = '';
51
52   @Input() diskType = '';
53
54   // Show filters for these columns, specify empty array to disable
55   @Input() filterColumns = [
56     'hostname',
57     'human_readable_type',
58     'available',
59     'sys_api.vendor',
60     'sys_api.model',
61     'sys_api.size'
62   ];
63
64   // Device table row selection type
65   @Input() selectionType: string = undefined;
66
67   @Output() filterChange = new EventEmitter<CdTableColumnFiltersChange>();
68
69   @Output() fetchInventory = new EventEmitter();
70
71   icons = Icons;
72   columns: Array<CdTableColumn> = [];
73   selection: CdTableSelection = new CdTableSelection();
74   permission: Permission;
75   tableActions: CdTableAction[];
76   fetchInventorySub: Subscription;
77
78   @Input() orchStatus: OrchestratorStatus = undefined;
79
80   actionOrchFeatures = {
81     identify: [OrchestratorFeature.DEVICE_BLINK_LIGHT]
82   };
83
84   constructor(
85     private authStorageService: AuthStorageService,
86     private dimlessBinary: DimlessBinaryPipe,
87     private modalService: ModalService,
88     private notificationService: NotificationService,
89     private orchService: OrchestratorService,
90     private hostService: HostService
91   ) {}
92
93   ngOnInit() {
94     this.permission = this.authStorageService.getPermissions().osd;
95     this.tableActions = [
96       {
97         permission: 'update',
98         icon: Icons.show,
99         click: () => this.identifyDevice(),
100         name: $localize`Identify`,
101         disable: (selection: CdTableSelection) => this.getDisable('identify', selection),
102         canBePrimary: (selection: CdTableSelection) => !selection.hasSingleSelection,
103         visible: () => _.isString(this.selectionType)
104       }
105     ];
106     const columns = [
107       {
108         name: $localize`Hostname`,
109         prop: 'hostname',
110         flexGrow: 1
111       },
112       {
113         name: $localize`Device path`,
114         prop: 'path',
115         flexGrow: 1
116       },
117       {
118         name: $localize`Type`,
119         prop: 'human_readable_type',
120         flexGrow: 1,
121         cellTransformation: CellTemplate.badge,
122         customTemplateConfig: {
123           map: {
124             hdd: { value: 'HDD', class: 'badge-hdd' },
125             ssd: { value: 'SSD', class: 'badge-ssd' }
126           }
127         }
128       },
129       {
130         name: $localize`Available`,
131         prop: 'available',
132         flexGrow: 1,
133         cellClass: 'text-center',
134         cellTransformation: CellTemplate.checkIcon
135       },
136       {
137         name: $localize`Vendor`,
138         prop: 'sys_api.vendor',
139         flexGrow: 1
140       },
141       {
142         name: $localize`Model`,
143         prop: 'sys_api.model',
144         flexGrow: 1
145       },
146       {
147         name: $localize`Size`,
148         prop: 'sys_api.size',
149         flexGrow: 1,
150         pipe: this.dimlessBinary
151       },
152       {
153         name: $localize`OSDs`,
154         prop: 'osd_ids',
155         flexGrow: 1,
156         cellTransformation: CellTemplate.badge,
157         customTemplateConfig: {
158           class: 'badge-dark',
159           prefix: 'osd.'
160         }
161       }
162     ];
163
164     this.columns = columns.filter((col: any) => {
165       return !this.hiddenColumns.includes(col.prop);
166     });
167
168     // init column filters
169     _.forEach(this.filterColumns, (prop) => {
170       const col = _.find(this.columns, { prop: prop });
171       if (col) {
172         col.filterable = true;
173       }
174
175       if (col?.prop === 'human_readable_type' && this.diskType === 'ssd') {
176         col.filterInitValue = this.diskType;
177       }
178
179       if (col?.prop === 'hostname' && this.hostname) {
180         col.filterInitValue = this.hostname;
181       }
182     });
183
184     if (this.fetchInventory.observers.length > 0) {
185       this.fetchInventorySub = this.table.fetchData.subscribe(() => {
186         this.fetchInventory.emit();
187       });
188     }
189   }
190
191   getDevices() {
192     if (this.showAvailDeviceOnly) {
193       this.hostService.inventoryDeviceList().subscribe(
194         (devices: InventoryDevice[]) => {
195           this.devices = _.filter(devices, 'available');
196           this.devices = [...this.devices];
197         },
198         () => {
199           this.devices = [];
200         }
201       );
202     } else {
203       this.devices = [...this.devices];
204     }
205   }
206
207   ngOnDestroy() {
208     if (this.fetchInventorySub) {
209       this.fetchInventorySub.unsubscribe();
210     }
211   }
212
213   onColumnFiltersChanged(event: CdTableColumnFiltersChange) {
214     this.filterChange.emit(event);
215   }
216
217   getDisable(action: 'identify', selection: CdTableSelection): boolean | string {
218     if (!selection.hasSingleSelection) {
219       return true;
220     }
221     return this.orchService.getTableActionDisableDesc(
222       this.orchStatus,
223       this.actionOrchFeatures[action]
224     );
225   }
226
227   updateSelection(selection: CdTableSelection) {
228     this.selection = selection;
229   }
230
231   identifyDevice() {
232     const selected = this.selection.first();
233     const hostname = selected.hostname;
234     const device = selected.path || selected.device_id;
235     this.modalService.show(FormModalComponent, {
236       titleText: $localize`Identify device ${device}`,
237       message: $localize`Please enter the duration how long to blink the LED.`,
238       fields: [
239         {
240           type: 'select',
241           name: 'duration',
242           value: 300,
243           required: true,
244           typeConfig: {
245             options: [
246               { text: $localize`1 minute`, value: 60 },
247               { text: $localize`2 minutes`, value: 120 },
248               { text: $localize`5 minutes`, value: 300 },
249               { text: $localize`10 minutes`, value: 600 },
250               { text: $localize`15 minutes`, value: 900 }
251             ]
252           }
253         }
254       ],
255       submitButtonText: $localize`Execute`,
256       onSubmit: (values: any) => {
257         this.hostService.identifyDevice(hostname, device, values.duration).subscribe(() => {
258           this.notificationService.show(
259             NotificationType.success,
260             $localize`Identifying '${device}' started on host '${hostname}'`
261           );
262         });
263       }
264     });
265   }
266 }