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