]> 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 17.2.0
[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 2
f67539c2
TL
3import { NgbNav } from '@ng-bootstrap/ng-bootstrap';
4import _ from 'lodash';
9f95a23c 5
f67539c2
TL
6import { HostService } from '~/app/shared/api/host.service';
7import { OsdService } from '~/app/shared/api/osd.service';
8import { CdTableColumn } from '~/app/shared/models/cd-table-column';
9f95a23c
TL
9import {
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})
22export 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 63You may be using an incompatible version of smartmontools. Version >= 7.0 of \
f67539c2 64smartmontools 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}