]>
Commit | Line | Data |
---|---|---|
f91f0fd5 | 1 | import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; |
9f95a23c | 2 | |
f67539c2 TL |
3 | import { NgbNav } from '@ng-bootstrap/ng-bootstrap'; |
4 | import _ from 'lodash'; | |
9f95a23c | 5 | |
f67539c2 TL |
6 | import { HostService } from '~/app/shared/api/host.service'; |
7 | import { OsdService } from '~/app/shared/api/osd.service'; | |
8 | import { CdTableColumn } from '~/app/shared/models/cd-table-column'; | |
9f95a23c TL |
9 | import { |
10 | HddSmartDataV1, | |
11 | NvmeSmartDataV1, | |
12 | SmartDataResult, | |
13 | SmartError, | |
14 | SmartErrorResult | |
f67539c2 | 15 | } from '~/app/shared/models/smart'; |
9f95a23c TL |
16 | |
17 | @Component({ | |
18 | selector: 'cd-smart-list', | |
19 | templateUrl: './smart-list.component.html', | |
20 | styleUrls: ['./smart-list.component.scss'] | |
21 | }) | |
22 | export class SmartListComponent implements OnInit, OnChanges { | |
f67539c2 TL |
23 | @ViewChild('innerNav') |
24 | nav: NgbNav; | |
f91f0fd5 | 25 | |
9f95a23c TL |
26 | @Input() |
27 | osdId: number = null; | |
28 | @Input() | |
29 | hostname: string = null; | |
30 | ||
31 | loading = false; | |
32 | incompatible = false; | |
33 | error = false; | |
34 | ||
35 | data: { [deviceId: string]: SmartDataResult | SmartErrorResult } = {}; | |
36 | ||
37 | smartDataColumns: CdTableColumn[]; | |
38 | ||
f91f0fd5 TL |
39 | isEmpty = _.isEmpty; |
40 | ||
f67539c2 | 41 | constructor(private osdService: OsdService, private hostService: HostService) {} |
9f95a23c TL |
42 | |
43 | isSmartError(data: any): data is SmartError { | |
44 | return _.get(data, 'error') !== undefined; | |
45 | } | |
46 | ||
47 | isNvmeSmartData(data: any): data is NvmeSmartDataV1 { | |
48 | return _.get(data, 'device.protocol', '').toLowerCase() === 'nvme'; | |
49 | } | |
50 | ||
51 | isHddSmartData(data: any): data is HddSmartDataV1 { | |
52 | return _.get(data, 'device.protocol', '').toLowerCase() === 'ata'; | |
53 | } | |
54 | ||
55 | private fetchData(data: any) { | |
56 | const result: { [deviceId: string]: SmartDataResult | SmartErrorResult } = {}; | |
57 | _.each(data, (smartData, deviceId) => { | |
58 | if (this.isSmartError(smartData)) { | |
59 | let userMessage = ''; | |
60 | if (smartData.smartctl_error_code === -22) { | |
f67539c2 TL |
61 | userMessage = $localize`Smartctl has received an unknown argument \ |
62 | (error code ${smartData.smartctl_error_code}). \ | |
e306af50 | 63 | You may be using an incompatible version of smartmontools. Version >= 7.0 of \ |
f67539c2 | 64 | smartmontools is required to successfully retrieve data.`; |
9f95a23c | 65 | } else { |
f67539c2 | 66 | userMessage = $localize`An error with error code ${smartData.smartctl_error_code} occurred.`; |
9f95a23c TL |
67 | } |
68 | const _result: SmartErrorResult = { | |
69 | error: smartData.error, | |
70 | smartctl_error_code: smartData.smartctl_error_code, | |
71 | smartctl_output: smartData.smartctl_output, | |
72 | userMessage: userMessage, | |
73 | device: smartData.dev, | |
74 | identifier: smartData.nvme_vendor | |
75 | }; | |
76 | result[deviceId] = _result; | |
77 | return; | |
78 | } | |
79 | ||
80 | // Prepare S.M.A.R.T data | |
81 | if (smartData.json_format_version[0] === 1) { | |
82 | // Version 1.x | |
83 | if (this.isHddSmartData(smartData)) { | |
84 | result[deviceId] = this.extractHddData(smartData); | |
85 | } else if (this.isNvmeSmartData(smartData)) { | |
86 | result[deviceId] = this.extractNvmeData(smartData); | |
87 | } | |
88 | return; | |
89 | } else { | |
90 | this.incompatible = true; | |
91 | } | |
92 | }); | |
93 | this.data = result; | |
94 | this.loading = false; | |
95 | } | |
96 | ||
97 | private extractNvmeData(smartData: NvmeSmartDataV1): SmartDataResult { | |
98 | const info = _.omitBy(smartData, (_value, key) => | |
99 | ['nvme_smart_health_information_log'].includes(key) | |
100 | ); | |
101 | return { | |
102 | info: info, | |
103 | smart: { | |
104 | nvmeData: smartData.nvme_smart_health_information_log | |
105 | }, | |
106 | device: smartData.device.name, | |
107 | identifier: smartData.serial_number | |
108 | }; | |
109 | } | |
110 | ||
111 | private extractHddData(smartData: HddSmartDataV1): SmartDataResult { | |
112 | const info = _.omitBy(smartData, (_value, key) => | |
113 | ['ata_smart_attributes', 'ata_smart_selective_self_test_log', 'ata_smart_data'].includes(key) | |
114 | ); | |
115 | return { | |
116 | info: info, | |
117 | smart: { | |
118 | attributes: smartData.ata_smart_attributes, | |
119 | data: smartData.ata_smart_data | |
120 | }, | |
121 | device: info.device.name, | |
122 | identifier: info.serial_number | |
123 | }; | |
124 | } | |
125 | ||
126 | private updateData() { | |
127 | this.loading = true; | |
128 | ||
129 | if (this.osdId !== null) { | |
f67539c2 TL |
130 | this.osdService.getSmartData(this.osdId).subscribe({ |
131 | next: this.fetchData.bind(this), | |
132 | error: (error) => { | |
133 | error.preventDefault(); | |
134 | this.error = error; | |
135 | this.loading = false; | |
136 | } | |
9f95a23c TL |
137 | }); |
138 | } else if (this.hostname !== null) { | |
f67539c2 TL |
139 | this.hostService.getSmartData(this.hostname).subscribe({ |
140 | next: this.fetchData.bind(this), | |
141 | error: (error) => { | |
142 | error.preventDefault(); | |
143 | this.error = error; | |
144 | this.loading = false; | |
145 | } | |
9f95a23c TL |
146 | }); |
147 | } | |
148 | } | |
149 | ||
150 | ngOnInit() { | |
151 | this.smartDataColumns = [ | |
f67539c2 TL |
152 | { prop: 'id', name: $localize`ID` }, |
153 | { prop: 'name', name: $localize`Name` }, | |
154 | { prop: 'raw.value', name: $localize`Raw` }, | |
155 | { prop: 'thresh', name: $localize`Threshold` }, | |
156 | { prop: 'value', name: $localize`Value` }, | |
157 | { prop: 'when_failed', name: $localize`When Failed` }, | |
158 | { prop: 'worst', name: $localize`Worst` } | |
9f95a23c TL |
159 | ]; |
160 | } | |
161 | ||
162 | ngOnChanges(changes: SimpleChanges): void { | |
163 | this.data = {}; // Clear previous data | |
164 | if (changes.osdId) { | |
165 | this.osdId = changes.osdId.currentValue; | |
166 | } else if (changes.hostname) { | |
167 | this.hostname = changes.hostname.currentValue; | |
168 | } | |
169 | this.updateData(); | |
170 | } | |
171 | } |