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