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