1 import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
3 import { NgbNav } from '@ng-bootstrap/ng-bootstrap';
4 import _ from 'lodash';
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';
15 } from '~/app/shared/models/smart';
18 selector: 'cd-smart-list',
19 templateUrl: './smart-list.component.html',
20 styleUrls: ['./smart-list.component.scss']
22 export class SmartListComponent implements OnInit, OnChanges {
23 @ViewChild('innerNav')
29 hostname: string = null;
35 data: { [deviceId: string]: SmartDataResult | SmartErrorResult } = {};
37 smartDataColumns: CdTableColumn[];
41 constructor(private osdService: OsdService, private hostService: HostService) {}
43 isSmartError(data: any): data is SmartError {
44 return _.get(data, 'error') !== undefined;
47 isNvmeSmartData(data: any): data is NvmeSmartDataV1 {
48 return _.get(data, 'device.protocol', '').toLowerCase() === 'nvme';
51 isHddSmartData(data: any): data is HddSmartDataV1 {
52 return _.get(data, 'device.protocol', '').toLowerCase() === 'ata';
55 private fetchData(data: any) {
56 const result: { [deviceId: string]: SmartDataResult | SmartErrorResult } = {};
57 _.each(data, (smartData, deviceId) => {
58 if (this.isSmartError(smartData)) {
60 if (smartData.smartctl_error_code === -22) {
61 userMessage = $localize`Smartctl has received an unknown argument \
62 (error code ${smartData.smartctl_error_code}). \
63 You may be using an incompatible version of smartmontools. Version >= 7.0 of \
64 smartmontools is required to successfully retrieve data.`;
66 userMessage = $localize`An error with error code ${smartData.smartctl_error_code} occurred.`;
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
76 result[deviceId] = _result;
80 // Prepare S.M.A.R.T data
81 if (smartData.json_format_version[0] === 1) {
83 if (this.isHddSmartData(smartData)) {
84 result[deviceId] = this.extractHddData(smartData);
85 } else if (this.isNvmeSmartData(smartData)) {
86 result[deviceId] = this.extractNvmeData(smartData);
90 this.incompatible = true;
97 private extractNvmeData(smartData: NvmeSmartDataV1): SmartDataResult {
98 const info = _.omitBy(smartData, (_value, key) =>
99 ['nvme_smart_health_information_log'].includes(key)
104 nvmeData: smartData.nvme_smart_health_information_log
106 device: smartData.device.name,
107 identifier: smartData.serial_number
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)
118 attributes: smartData.ata_smart_attributes,
119 data: smartData.ata_smart_data
121 device: info.device.name,
122 identifier: info.serial_number
126 private updateData() {
129 if (this.osdId !== null) {
130 this.osdService.getSmartData(this.osdId).subscribe({
131 next: this.fetchData.bind(this),
133 error.preventDefault();
135 this.loading = false;
138 } else if (this.hostname !== null) {
139 this.hostService.getSmartData(this.hostname).subscribe({
140 next: this.fetchData.bind(this),
142 error.preventDefault();
144 this.loading = false;
151 this.smartDataColumns = [
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` }
162 ngOnChanges(changes: SimpleChanges): void {
163 this.data = {}; // Clear previous data
165 this.osdId = changes.osdId.currentValue;
166 } else if (changes.hostname) {
167 this.hostname = changes.hostname.currentValue;