From d2da65db0af59562119cec77b5a05a4ae42547d7 Mon Sep 17 00:00:00 2001 From: Nizamudeen A Date: Sun, 8 May 2022 19:57:34 +0530 Subject: [PATCH] mgr/dashboard: smart data for devices with scsi protocol In the dashboard, we've been showing smart data for hdd devices with ata protocol only. Otherwise we show a No Smart Data found error which is clearly misleading since Smart Data is returned even in the api call. So this PR is trying to show the smart data for hdd devices that uses scsi protocol too. Fixes: https://tracker.ceph.com/issues/55574 Signed-off-by: Nizamudeen A (cherry picked from commit 4fab663983ec34bb9bafa3f8104f2b7a70b58877) --- ... smart_data_version_1_0_ata_response.json} | 0 .../smart_data_version_1_0_scsi_response.json | 208 ++++++++++++++++++ .../smart-list/smart-list.component.html | 6 +- .../smart-list/smart-list.component.spec.ts | 40 +++- .../shared/smart-list/smart-list.component.ts | 57 ++++- .../frontend/src/app/shared/models/smart.ts | 26 ++- 6 files changed, 319 insertions(+), 18 deletions(-) rename src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/{smart_data_version_1_0_hdd_response.json => smart_data_version_1_0_ata_response.json} (100%) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_scsi_response.json diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_hdd_response.json b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_ata_response.json similarity index 100% rename from src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_hdd_response.json rename to src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_ata_response.json diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_scsi_response.json b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_scsi_response.json new file mode 100644 index 0000000000000..dfbe580c79f80 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/fixtures/smart_data_version_1_0_scsi_response.json @@ -0,0 +1,208 @@ +{ + "WDC_WUH721818AL5204_012345689": { + "device": { + "info_name": "/dev/sdf", + "name": "/dev/sdf", + "protocol": "SCSI", + "type": "scsi" + }, + "device_type": { + "name": "disk", + "scsi_value": 0 + }, + "form_factor": { + "name": "3.5 inches", + "scsi_value": 2 + }, + "json_format_version": [ + 1, + 0 + ], + "local_time": { + "asctime": "Sun May 8 14:21:11 2022 UTC", + "time_t": 1652019671 + }, + "logical_block_size": 512, + "model_name": "WDC WUH721818AL5204", + "nvme_smart_health_information_add_log_error": "nvme returned an error: sudo: exit status: 231", + "nvme_smart_health_information_add_log_error_code": -22, + "nvme_vendor": "wdc", + "physical_block_size": 4096, + "power_on_time": { + "hours": 1719, + "minutes": 55 + }, + "product": "WUH721818AL5204", + "revision": "C232", + "rotation_rate": 7200, + "scsi_error_counter_log": { + "read": { + "correction_algorithm_invocations": 1001, + "errors_corrected_by_eccdelayed": 0, + "errors_corrected_by_eccfast": 0, + "errors_corrected_by_rereads_rewrites": 0, + "gigabytes_processed": "8519.006", + "total_errors_corrected": 0, + "total_uncorrected_errors": 0 + }, + "verify": { + "correction_algorithm_invocations": 261, + "errors_corrected_by_eccdelayed": 0, + "errors_corrected_by_eccfast": 0, + "errors_corrected_by_rereads_rewrites": 0, + "gigabytes_processed": "0.000", + "total_errors_corrected": 0, + "total_uncorrected_errors": 0 + }, + "write": { + "correction_algorithm_invocations": 25720, + "errors_corrected_by_eccdelayed": 0, + "errors_corrected_by_eccfast": 0, + "errors_corrected_by_rereads_rewrites": 0, + "gigabytes_processed": "146241.629", + "total_errors_corrected": 0, + "total_uncorrected_errors": 0 + } + }, + "scsi_grown_defect_list": 0, + "scsi_version": "SPC-5", + "serial_number": "0123456789", + "smart_status": { + "passed": true + }, + "smartctl": { + "argv": [ + "smartctl", + "-x", + "--json=o", + "/dev/sdf" + ], + "build_info": "(local build)", + "exit_status": 0, + "output": [ + "smartctl 7.1 2020-04-05 r5049 [x86_64-linux-4.18.0-348.2.1.el8_5.x86_64] (local build)", + "Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org", + "", + "=== START OF INFORMATION SECTION ===", + "Vendor: WDC", + "Product: WUH721818AL5204", + "Revision: C232", + "Compliance: SPC-5", + "User Capacity: 18,000,207,937,536 bytes [18.0 TB]", + "Logical block size: 512 bytes", + "Physical block size: 4096 bytes", + "LU is fully provisioned", + "Rotation Rate: 7200 rpm", + "Form Factor: 3.5 inches", + "Logical Unit id: 0xffffffffffffffffffffffff", + "Serial number: 0123456789", + "Device type: disk", + "Transport protocol: SAS (SPL-3)", + "Local Time is: Sun May 8 14:21:11 2022 UTC", + "SMART support is: Available - device has SMART capability.", + "SMART support is: Enabled", + "Temperature Warning: Enabled", + "Read Cache is: Enabled", + "Writeback Cache is: Enabled", + "", + "=== START OF READ SMART DATA SECTION ===", + "SMART Health Status: OK", + "", + "Grown defects during certification ", + "Total blocks reassigned during format ", + "Total new blocks reassigned ", + "Power on minutes since format ", + "Current Drive Temperature: 38 C", + "Drive Trip Temperature: 85 C", + "", + "Manufactured in week 43 of year 2021", + "Specified cycle count over device lifetime: 50000", + "Accumulated start-stop cycles: 9", + "Specified load-unload count over device lifetime: 600000", + "Accumulated load-unload cycles: 74", + "Elements in grown defect list: 0", + "", + "Error counter log:", + " Errors Corrected by Total Correction Gigabytes Total", + " ECC rereads/ errors algorithm processed uncorrected", + " fast | delayed rewrites corrected invocations [10^9 bytes] errors", + "read: 0 0 0 0 1001 8519.006 0", + "write: 0 0 0 0 25720 146241.629 0", + "verify: 0 0 0 0 261 0.000 0", + "", + "Non-medium error count: 0", + "", + "No Self-tests have been logged", + "", + "Background scan results log", + " Status: waiting until BMS interval timer expires", + " Accumulated power on time, hours:minutes 1719:55 [103195 minutes]", + " Number of background scans performed: 5, scan progress: 0.00%", + " Number of background medium scans performed: 5", + "", + "Protocol Specific port log page for SAS SSP", + "relative target port id = 1", + " generation code = 3", + " number of phys = 1", + " phy identifier = 0", + " attached device type: expander device", + " attached reason: loss of dword synchronization", + " reason: unknown", + " negotiated logical link rate: phy enabled; 12 Gbps", + " attached initiator port: ssp=0 stp=0 smp=1", + " attached target port: ssp=0 stp=0 smp=1", + " SAS address = 0xffffffffffffffffffffffff", + " attached SAS address = 0xffffffffffffffffffffffff", + " attached phy identifier = 0", + " Invalid DWORD count = 0", + " Running disparity error count = 0", + " Loss of DWORD synchronization = 0", + " Phy reset problem = 0", + " Phy event descriptors:", + " Invalid word count: 0", + " Running disparity error count: 0", + " Loss of dword synchronization count: 0", + " Phy reset problem count: 0", + "relative target port id = 2", + " generation code = 3", + " number of phys = 1", + " phy identifier = 1", + " attached device type: expander device", + " attached reason: power on", + " reason: unknown", + " negotiated logical link rate: phy enabled; 12 Gbps", + " attached initiator port: ssp=0 stp=0 smp=1", + " attached target port: ssp=0 stp=0 smp=1", + " SAS address = 0xffffffffffffffffffffffff", + " attached SAS address = 0xffffffffffffffffffffffff", + " attached phy identifier = 0", + " Invalid DWORD count = 0", + " Running disparity error count = 0", + " Loss of DWORD synchronization = 0", + " Phy reset problem = 0", + " Phy event descriptors:", + " Invalid word count: 0", + " Running disparity error count: 0", + " Loss of dword synchronization count: 0", + " Phy reset problem count: 0", + "" + ], + "platform_info": "x86_64-linux-4.18.0-348.2.1.el8_5.x86_64", + "svn_revision": "5049", + "version": [ + 7, + 1 + ] + }, + "temperature": { + "current": 38, + "drive_trip": 85 + }, + "user_capacity": { + "blocks": 35156656128, + "bytes": 18000207937536 + }, + "vendor": "WDC" + } + } + \ No newline at end of file diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html index 063d0c29af81d..805d7558e3df7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html @@ -79,11 +79,15 @@ [data]="device.value.smart.attributes.table" updateSelectionOnRefresh="never" [columns]="smartDataColumns"> + - No SMART data available for this device. diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts index 2827d67ec87f8..54c436ca6f328 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts @@ -10,7 +10,12 @@ import { NgxPipeFunctionModule } from 'ngx-pipe-function'; import { of } from 'rxjs'; import { OsdService } from '~/app/shared/api/osd.service'; -import { HddSmartDataV1, NvmeSmartDataV1, SmartDataResult } from '~/app/shared/models/smart'; +import { + AtaSmartDataV1, + IscsiSmartDataV1, + NvmeSmartDataV1, + SmartDataResult +} from '~/app/shared/models/smart'; import { SharedModule } from '~/app/shared/shared.module'; import { configureTestBed } from '~/testing/unit-test-helper'; import { SmartListComponent } from './smart-list.component'; @@ -20,8 +25,9 @@ describe('OsdSmartListComponent', () => { let fixture: ComponentFixture; let osdService: OsdService; - const SMART_DATA_HDD_VERSION_1_0: HddSmartDataV1 = require('./fixtures/smart_data_version_1_0_hdd_response.json'); + const SMART_DATA_ATA_VERSION_1_0: AtaSmartDataV1 = require('./fixtures/smart_data_version_1_0_ata_response.json'); const SMART_DATA_NVME_VERSION_1_0: NvmeSmartDataV1 = require('./fixtures/smart_data_version_1_0_nvme_response.json'); + const SMART_DATA_SCSI_VERSION_1_0: IscsiSmartDataV1 = require('./fixtures/smart_data_version_1_0_scsi_response.json'); /** * Sets attributes for _all_ returned devices according to the given path. The syntax is the same @@ -38,7 +44,7 @@ describe('OsdSmartListComponent', () => { */ const patchData = (path: string, newValue: any): any => { return _.reduce( - _.cloneDeep(SMART_DATA_HDD_VERSION_1_0), + _.cloneDeep(SMART_DATA_ATA_VERSION_1_0), (result: object, dataObj, deviceId) => { result[deviceId] = _.set(dataObj, path, newValue); return result; @@ -52,18 +58,21 @@ describe('OsdSmartListComponent', () => { * of `OsdService`. Determines which data is returned. */ const initializeComponentWithData = ( - dataType: 'hdd_v1' | 'nvme_v1', + dataType: 'hdd_v1' | 'nvme_v1' | 'hdd_v1_scsi', patch: { [path: string]: any } = null, simpleChanges?: SimpleChanges ) => { - let data: HddSmartDataV1 | NvmeSmartDataV1; + let data: AtaSmartDataV1 | NvmeSmartDataV1 | IscsiSmartDataV1; switch (dataType) { case 'hdd_v1': - data = SMART_DATA_HDD_VERSION_1_0; + data = SMART_DATA_ATA_VERSION_1_0; break; case 'nvme_v1': data = SMART_DATA_NVME_VERSION_1_0; break; + case 'hdd_v1_scsi': + data = SMART_DATA_SCSI_VERSION_1_0; + break; } if (_.isObject(patch)) { @@ -131,7 +140,7 @@ describe('OsdSmartListComponent', () => { expect(component).toBeTruthy(); }); - describe('tests HDD version 1.x', () => { + describe('tests ATA version 1.x', () => { beforeEach(() => initializeComponentWithData('hdd_v1')); it('should return with proper keys', () => { @@ -169,6 +178,23 @@ describe('OsdSmartListComponent', () => { }); }); + describe('tests SCSI version 1.x', () => { + beforeEach(() => initializeComponentWithData('hdd_v1_scsi')); + + it('should return with proper keys', () => { + _.each(component.data, (smartData, _deviceId) => { + expect(_.keys(smartData)).toEqual(['info', 'smart', 'device', 'identifier']); + }); + }); + + it('should not contain excluded keys in `info`', () => { + const excludes = ['scsi_error_counter_log', 'scsi_grown_defect_list']; + _.each(component.data, (smartData: SmartDataResult, _deviceId) => { + _.each(excludes, (exclude) => expect(smartData.info[exclude]).toBeUndefined()); + }); + }); + }); + it('should not work for version 2.x', () => { initializeComponentWithData('nvme_v1', { json_format_version: [2, 0] }); expect(component.data).toEqual({}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts index 298e22b86d02f..abfdcfe5be57f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.ts @@ -7,7 +7,8 @@ import { HostService } from '~/app/shared/api/host.service'; import { OsdService } from '~/app/shared/api/osd.service'; import { CdTableColumn } from '~/app/shared/models/cd-table-column'; import { - HddSmartDataV1, + AtaSmartDataV1, + IscsiSmartDataV1, NvmeSmartDataV1, SmartDataResult, SmartError, @@ -35,6 +36,7 @@ export class SmartListComponent implements OnInit, OnChanges { data: { [deviceId: string]: SmartDataResult | SmartErrorResult } = {}; smartDataColumns: CdTableColumn[]; + scsiSmartDataColumns: CdTableColumn[]; isEmpty = _.isEmpty; @@ -48,10 +50,14 @@ export class SmartListComponent implements OnInit, OnChanges { return _.get(data, 'device.protocol', '').toLowerCase() === 'nvme'; } - isHddSmartData(data: any): data is HddSmartDataV1 { + isAtaSmartData(data: any): data is AtaSmartDataV1 { return _.get(data, 'device.protocol', '').toLowerCase() === 'ata'; } + isIscsiSmartData(data: any): data is IscsiSmartDataV1 { + return _.get(data, 'device.protocol', '').toLowerCase() === 'scsi'; + } + private fetchData(data: any) { const result: { [deviceId: string]: SmartDataResult | SmartErrorResult } = {}; _.each(data, (smartData, deviceId) => { @@ -76,12 +82,13 @@ smartmontools is required to successfully retrieve data.`; result[deviceId] = _result; return; } - // Prepare S.M.A.R.T data if (smartData.json_format_version[0] === 1) { // Version 1.x - if (this.isHddSmartData(smartData)) { - result[deviceId] = this.extractHddData(smartData); + if (this.isAtaSmartData(smartData)) { + result[deviceId] = this.extractAtaData(smartData); + } else if (this.isIscsiSmartData(smartData)) { + result[deviceId] = this.extractIscsiData(smartData); } else if (this.isNvmeSmartData(smartData)) { result[deviceId] = this.extractNvmeData(smartData); } @@ -95,7 +102,7 @@ smartmontools is required to successfully retrieve data.`; } private extractNvmeData(smartData: NvmeSmartDataV1): SmartDataResult { - const info = _.omitBy(smartData, (_value, key) => + const info = _.omitBy(smartData, (_value: string, key: string) => ['nvme_smart_health_information_log'].includes(key) ); return { @@ -108,8 +115,23 @@ smartmontools is required to successfully retrieve data.`; }; } - private extractHddData(smartData: HddSmartDataV1): SmartDataResult { - const info = _.omitBy(smartData, (_value, key) => + private extractIscsiData(smartData: IscsiSmartDataV1): SmartDataResult { + const info = _.omitBy(smartData, (_value: string, key: string) => + ['scsi_error_counter_log', 'scsi_grown_defect_list'].includes(key) + ); + return { + info: info, + smart: { + scsi_error_counter_log: smartData.scsi_error_counter_log, + scsi_grown_defect_list: smartData.scsi_grown_defect_list + }, + device: info.device.name, + identifier: info.serial_number + }; + } + + private extractAtaData(smartData: AtaSmartDataV1): SmartDataResult { + const info = _.omitBy(smartData, (_value: string, key: string) => ['ata_smart_attributes', 'ata_smart_selective_self_test_log', 'ata_smart_data'].includes(key) ); return { @@ -157,6 +179,25 @@ smartmontools is required to successfully retrieve data.`; { prop: 'when_failed', name: $localize`When Failed` }, { prop: 'worst', name: $localize`Worst` } ]; + + this.scsiSmartDataColumns = [ + { + prop: 'correction_algorithm_invocations', + name: $localize`Correction Algorithm Invocations` + }, + { + prop: 'errors_corrected_by_eccdelayed', + name: $localize`Errors Corrected by ECC (Delayed)` + }, + { prop: 'errors_corrected_by_eccfast', name: $localize`Errors Corrected by ECC (Fast)` }, + { + prop: 'errors_corrected_by_rereads_rewrites', + name: $localize`Errors Corrected by Rereads/Rewrites` + }, + { prop: 'gigabytes_processed', name: $localize`Gigabyes Processed` }, + { prop: 'total_errors_corrected', name: $localize`Total Errors Corrected` }, + { prop: 'total_uncorrected_errors', name: $localize`Total Errors Uncorrected` } + ]; } ngOnChanges(changes: SimpleChanges): void { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts index 2f7f82c4b2ae9..f553652bcd9a8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/smart.ts @@ -77,10 +77,30 @@ interface SmartCtlBaseDataV1 { user_capacity: { blocks: number; bytes: number }; } +export interface RVWAttributes { + correction_algorithm_invocations: number; + errors_corrected_by_eccdelayed: number; + errors_corrected_by_eccfast: number; + errors_corrected_by_rereads_rewrites: number; + gigabytes_processed: number; + total_errors_corrected: number; + total_uncorrected_errors: number; +} + +/** + * Result structure of `smartctl` applied on an SCSI. Returned by the back-end. + */ +export interface IscsiSmartDataV1 extends SmartCtlBaseDataV1 { + scsi_error_counter_log: { + read: RVWAttributes[]; + }; + scsi_grown_defect_list: number; +} + /** * Result structure of `smartctl` applied on an HDD. Returned by the back-end. */ -export interface HddSmartDataV1 extends SmartCtlBaseDataV1 { +export interface AtaSmartDataV1 extends SmartCtlBaseDataV1 { ata_sct_capabilities: { data_table_supported: boolean; error_recovery_control_supported: boolean; @@ -204,7 +224,7 @@ interface SmartBasicResult { * contain the structure for a HDD, NVMe or an error. */ export interface SmartDataResponseV1 { - [deviceId: string]: HddSmartDataV1 | NvmeSmartDataV1 | SmartError; + [deviceId: string]: AtaSmartDataV1 | NvmeSmartDataV1 | SmartError; } /** @@ -216,6 +236,8 @@ export interface SmartDataResult extends SmartBasicResult { attributes?: any; data?: any; nvmeData?: any; + scsi_error_counter_log?: any; + scsi_grown_defect_list?: any; }; } -- 2.39.5