[pve-devel] [PATCH storage 2/2] smartctl: use json parsing
Oguz Bektas
o.bektas at proxmox.com
Thu Apr 1 15:40:55 CEST 2021
adapt the smartctl endpoint to run smartctl with the --json or -j flag
to parse it more reasonably.
also add the $format parameter to assist in switching to the new json
parsed output for nvme devices in PVE 7.0 (until 7.0 removing the 'text'
field completely would be a breaking change, so we still default to the
text field but with the parsed "key : value" pairs instead of raw
smartctl output)
for the unit tests from now we need to collect the smartctl outputs with
the json flag. the current tests cover ssd_smart and nvme_smart cases.
Signed-off-by: Oguz Bektas <o.bektas at proxmox.com>
----
RFC -> PATCH:
* split patch for removing old tests
* incorporate stefan's and thomas' recommendations & nits (thanks!!)
* fix issue with array fields not being parsed
* add array element in nvme unit test (temperature_sensor)
PVE/API2/Disks.pm | 9 +-
PVE/Diskmanage.pm | 120 +++---
test/disk_tests/nvme_smart/disklist | 1 +
.../nvme_smart/disklist_expected.json | 16 +
test/disk_tests/nvme_smart/nvme0/model | 1 +
test/disk_tests/nvme_smart/nvme0_smart | 65 ++++
test/disk_tests/nvme_smart/nvme0n1/device | 1 +
.../nvme_smart/nvme0n1/queue/rotational | 1 +
test/disk_tests/nvme_smart/nvme0n1/size | 1 +
.../nvme_smart/nvme0n1_smart_expected.json | 6 +
test/disk_tests/nvme_smart/nvme0n1_udevadm | 18 +
test/disk_tests/ssd_smart/disklist | 1 +
.../ssd_smart/disklist_expected.json | 16 +
test/disk_tests/ssd_smart/sda/device/vendor | 1 +
.../disk_tests/ssd_smart/sda/queue/rotational | 1 +
test/disk_tests/ssd_smart/sda/size | 1 +
test/disk_tests/ssd_smart/sda_smart | 352 ++++++++++++++++++
.../ssd_smart/sda_smart_expected.json | 146 ++++++++
test/disk_tests/ssd_smart/sda_udevadm | 11 +
test/disklist_test.pm | 4 +-
20 files changed, 714 insertions(+), 58 deletions(-)
create mode 100644 test/disk_tests/nvme_smart/disklist
create mode 100644 test/disk_tests/nvme_smart/disklist_expected.json
create mode 100644 test/disk_tests/nvme_smart/nvme0/model
create mode 100644 test/disk_tests/nvme_smart/nvme0_smart
create mode 120000 test/disk_tests/nvme_smart/nvme0n1/device
create mode 100644 test/disk_tests/nvme_smart/nvme0n1/queue/rotational
create mode 100644 test/disk_tests/nvme_smart/nvme0n1/size
create mode 100644 test/disk_tests/nvme_smart/nvme0n1_smart_expected.json
create mode 100644 test/disk_tests/nvme_smart/nvme0n1_udevadm
create mode 100644 test/disk_tests/ssd_smart/disklist
create mode 100644 test/disk_tests/ssd_smart/disklist_expected.json
create mode 100644 test/disk_tests/ssd_smart/sda/device/vendor
create mode 100644 test/disk_tests/ssd_smart/sda/queue/rotational
create mode 100644 test/disk_tests/ssd_smart/sda/size
create mode 100644 test/disk_tests/ssd_smart/sda_smart
create mode 100644 test/disk_tests/ssd_smart/sda_smart_expected.json
create mode 100644 test/disk_tests/ssd_smart/sda_udevadm
diff --git a/PVE/API2/Disks.pm b/PVE/API2/Disks.pm
index 33bca76..a453efc 100644
--- a/PVE/API2/Disks.pm
+++ b/PVE/API2/Disks.pm
@@ -195,6 +195,12 @@ __PACKAGE__->register_method ({
description => "If true returns only the health status",
optional => 1,
},
+ format => {
+ description => "Return json or text",
+ type => 'string',
+ enum => ['text', 'json'],
+ optional => 1,
+ },
},
},
returns => {
@@ -204,6 +210,7 @@ __PACKAGE__->register_method ({
type => { type => 'string', optional => 1 },
attributes => { type => 'array', optional => 1},
text => { type => 'string', optional => 1 },
+ json => { type => 'string', optional => 1 },
},
},
code => sub {
@@ -211,7 +218,7 @@ __PACKAGE__->register_method ({
my $disk = PVE::Diskmanage::verify_blockdev_path($param->{disk});
- my $result = PVE::Diskmanage::get_smart_data($disk, $param->{healthonly});
+ my $result = PVE::Diskmanage::get_smart_data($disk, $param->{healthonly}, $param->{format});
$result->{health} = 'UNKNOWN' if !defined $result->{health};
$result = { health => $result->{health} } if $param->{healthonly};
diff --git a/PVE/Diskmanage.pm b/PVE/Diskmanage.pm
index 64bb813..3f433fb 100644
--- a/PVE/Diskmanage.pm
+++ b/PVE/Diskmanage.pm
@@ -81,11 +81,12 @@ sub disk_is_used {
}
sub get_smart_data {
- my ($disk, $healthonly) = @_;
+ my ($disk, $healthonly, $format) = @_;
assert_blockdev($disk);
my $smartdata = {};
my $type;
+ $format //= 'text';
my $returncode = 0;
@@ -95,70 +96,79 @@ sub get_smart_data {
or die "failed to get nvme controller device for $disk\n");
}
- my $cmd = [$SMARTCTL, '-H'];
- push @$cmd, '-A', '-f', 'brief' if !$healthonly;
+ my $cmd = [$SMARTCTL, '-j', '-H'];
+ push @$cmd, '-Afbrief' if !$healthonly;
push @$cmd, $disk;
+ my $smart_result = '';
eval {
- $returncode = run_command($cmd, noerr => 1, outfunc => sub{
- my ($line) = @_;
+ $returncode = run_command($cmd, noerr => 1, outfunc => sub { $smart_result .= shift });
+ };
+ my $err = $@;
-# ATA SMART attributes, e.g.:
-# ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE
-# 1 Raw_Read_Error_Rate POSR-K 100 100 000 - 0
-#
-# SAS and NVME disks, e.g.:
-# Data Units Written: 5,584,952 [2.85 TB]
-# Accumulated start-stop cycles: 34
-
- if (defined($type) && $type eq 'ata' && $line =~ m/^([ \d]{2}\d)\s+(\S+)\s+(\S{6})\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
- my $entry = {};
-
- $entry->{name} = $2 if defined $2;
- $entry->{flags} = $3 if defined $3;
- # the +0 makes a number out of the strings
- $entry->{value} = $4+0 if defined $4;
- $entry->{worst} = $5+0 if defined $5;
- # some disks report the default threshold as --- instead of 000
- if (defined($6) && $6 eq '---') {
- $entry->{threshold} = 0;
- } else {
- $entry->{threshold} = $6+0 if defined $6;
- }
- $entry->{fail} = $7 if defined $7;
- $entry->{raw} = $8 if defined $8;
- $entry->{id} = $1 if defined $1;
- push @{$smartdata->{attributes}}, $entry;
- } elsif ($line =~ m/(?:Health Status|self\-assessment test result): (.*)$/ ) {
- $smartdata->{health} = $1;
- } elsif ($line =~ m/Vendor Specific SMART Attributes with Thresholds:/) {
- $type = 'ata';
- delete $smartdata->{text};
- } elsif ($line =~ m/=== START OF (READ )?SMART DATA SECTION ===/) {
- $type = 'text';
- } elsif (defined($type) && $type eq 'text') {
- $smartdata->{text} = '' if !defined $smartdata->{text};
- $smartdata->{text} .= "$line\n";
- # extract wearout from nvme/sas text, allow for decimal values
- if ($line =~ m/Percentage Used(?: endurance indicator)?:\s*(\d+(?:\.\d+)?)\%/i) {
- $smartdata->{wearout} = 100 - $1;
+ my $json_result = decode_json($smart_result);
+
+ my $smart_health = $json_result->{smart_status}->{passed};
+ if (JSON::is_bool($smart_health)) {
+ $smart_health = $smart_health ? "PASSED" : "FAILED";
+ } else {
+ $smart_health = 'UNKNOWN';
+ }
+
+ $smartdata->{health} = $smart_health;
+
+ $type = $json_result->{device}->{type};
+ if ($type eq 'nvme') {
+ $smartdata->{type} = $format; # text or json but FIXME: remove in PVE 7.0 and use json
+
+ my $nvme_info = $json_result->{nvme_smart_health_information_log};
+
+ if (!$healthonly) {
+ $smartdata->{wearout} = 100.0 - $nvme_info->{percentage_used};
+ }
+
+ my $add_key = sub {
+ my ($key, $value) = @_;
+ if ($format eq 'text') {
+ $smartdata->{text} .= "$key : $value\n";
+ }
+ };
+
+ foreach my $key (sort keys %{$nvme_info}) {
+ my $value = $nvme_info->{$key};
+ # some fields can also be arrays
+ # e.g. temperature_sensor
+ if (ref($value) eq 'ARRAY') {
+ while (my $index = each(@$value)) {
+ $add_key->("${key}_${index}", $value->[$index]);
}
- } elsif ($line =~ m/SMART Disabled/) {
- $smartdata->{health} = "SMART Disabled";
+ } else {
+ $add_key->($key, $value);
}
- });
- };
- my $err = $@;
+ }
+ delete $smartdata->{attributes}; # this is for ata disks only
+ } else {
+ $smartdata->{type} = 'ata';
+ foreach my $attribute (@{$json_result->{ata_smart_attributes}->{table}}) {
+ # we need to override or delete some fields to fit expected json schema of unit tests
+ my $flags_string = $attribute->{flags}->{string};
+ $flags_string =~ s/\s+$//;
+ $attribute->{flags} = $flags_string;
+ $attribute->{raw} = $attribute->{raw}->{string};
+ $attribute->{threshold} = delete $attribute->{thresh};
+ $attribute->{fail} = ${attribute}->{when_failed} eq "" ? "-" : ${attribute}->{when_failed};
+ delete ${attribute}->{when_failed};
+
+ push @{$smartdata->{attributes}}, $attribute;
+ }
+ my @sorted_attributes = sort { $a->{id} <=> $b->{id} } @{$smartdata->{attributes}};
+ @{$smartdata->{attributes}} = @sorted_attributes;
+ }
- # bit 0 and 1 mark an severe smartctl error
- # all others are for disk status, so ignore them
- # see smartctl(8)
- if ((defined($returncode) && ($returncode & 0b00000011)) || $err) {
+ if ($returncode & 0b00000011 || $err) {
die "Error getting S.M.A.R.T. data: Exit code: $returncode\n";
}
- $smartdata->{type} = $type;
-
return $smartdata;
}
diff --git a/test/disk_tests/nvme_smart/disklist b/test/disk_tests/nvme_smart/disklist
new file mode 100644
index 0000000..d00b90e
--- /dev/null
+++ b/test/disk_tests/nvme_smart/disklist
@@ -0,0 +1 @@
+nvme0n1
diff --git a/test/disk_tests/nvme_smart/disklist_expected.json b/test/disk_tests/nvme_smart/disklist_expected.json
new file mode 100644
index 0000000..4d1c92f
--- /dev/null
+++ b/test/disk_tests/nvme_smart/disklist_expected.json
@@ -0,0 +1,16 @@
+{
+ "nvme0n1" : {
+ "wearout" : 69,
+ "vendor" : "unknown",
+ "size" : 512000,
+ "health" : "PASSED",
+ "serial" : "unknown",
+ "model" : "NVME MODEL 1",
+ "rpm" : 0,
+ "osdid" : -1,
+ "devpath" : "/dev/nvme0n1",
+ "gpt" : 0,
+ "wwn" : "unknown",
+ "type" : "nvme"
+ }
+}
diff --git a/test/disk_tests/nvme_smart/nvme0/model b/test/disk_tests/nvme_smart/nvme0/model
new file mode 100644
index 0000000..9bd6eba
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0/model
@@ -0,0 +1 @@
+NVME MODEL 1
diff --git a/test/disk_tests/nvme_smart/nvme0_smart b/test/disk_tests/nvme_smart/nvme0_smart
new file mode 100644
index 0000000..3f3c799
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0_smart
@@ -0,0 +1,65 @@
+{
+ "json_format_version": [
+ 1,
+ 0
+ ],
+ "smartctl": {
+ "version": [
+ 7,
+ 2
+ ],
+ "svn_revision": "5155",
+ "platform_info": "x86_64-linux-5.11.7-1-pve",
+ "build_info": "(local build)",
+ "argv": [
+ "smartctl",
+ "-j",
+ "-H",
+ "-Afbrief",
+ "/dev/nvme0"
+ ],
+ "exit_status": 0
+ },
+ "device": {
+ "name": "/dev/nvme0",
+ "info_name": "/dev/nvme0",
+ "type": "nvme",
+ "protocol": "NVMe"
+ },
+ "smart_status": {
+ "passed": true,
+ "nvme": {
+ "value": 0
+ }
+ },
+ "nvme_smart_health_information_log": {
+ "critical_warning": 0,
+ "temperature": 35,
+ "available_spare": 100,
+ "available_spare_threshold": 10,
+ "percentage_used": 31,
+ "data_units_read": 4546105,
+ "data_units_written": 16623231,
+ "host_reads": 154011524,
+ "host_writes": 282144186,
+ "controller_busy_time": 355,
+ "power_cycles": 514,
+ "power_on_hours": 2491,
+ "unsafe_shutdowns": 38,
+ "media_errors": 0,
+ "num_err_log_entries": 0,
+ "warning_temp_time": 0,
+ "critical_comp_time": 0,
+ "temperature_sensors": [
+ 36,
+ 34
+ ]
+ },
+ "temperature": {
+ "current": 35
+ },
+ "power_cycle_count": 514,
+ "power_on_time": {
+ "hours": 2491
+ }
+}
diff --git a/test/disk_tests/nvme_smart/nvme0n1/device b/test/disk_tests/nvme_smart/nvme0n1/device
new file mode 120000
index 0000000..e890f3e
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0n1/device
@@ -0,0 +1 @@
+../nvme0
\ No newline at end of file
diff --git a/test/disk_tests/nvme_smart/nvme0n1/queue/rotational b/test/disk_tests/nvme_smart/nvme0n1/queue/rotational
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0n1/queue/rotational
@@ -0,0 +1 @@
+0
diff --git a/test/disk_tests/nvme_smart/nvme0n1/size b/test/disk_tests/nvme_smart/nvme0n1/size
new file mode 100644
index 0000000..83b33d2
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0n1/size
@@ -0,0 +1 @@
+1000
diff --git a/test/disk_tests/nvme_smart/nvme0n1_smart_expected.json b/test/disk_tests/nvme_smart/nvme0n1_smart_expected.json
new file mode 100644
index 0000000..e3f0ce0
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0n1_smart_expected.json
@@ -0,0 +1,6 @@
+{
+ "text" : "available_spare : 100\navailable_spare_threshold : 10\ncontroller_busy_time : 355\ncritical_comp_time : 0\ncritical_warning : 0\ndata_units_read : 4546105\ndata_units_written : 16623231\nhost_reads : 154011524\nhost_writes : 282144186\nmedia_errors : 0\nnum_err_log_entries : 0\npercentage_used : 31\npower_cycles : 514\npower_on_hours : 2491\ntemperature : 35\ntemperature_sensors_0 : 36\ntemperature_sensors_1 : 34\nunsafe_shutdowns : 38\nwarning_temp_time : 0\n",
+ "health" : "PASSED",
+ "type" : "text",
+ "wearout": 69
+}
diff --git a/test/disk_tests/nvme_smart/nvme0n1_udevadm b/test/disk_tests/nvme_smart/nvme0n1_udevadm
new file mode 100644
index 0000000..36c78ce
--- /dev/null
+++ b/test/disk_tests/nvme_smart/nvme0n1_udevadm
@@ -0,0 +1,18 @@
+
+P: /devices/pci0000:00/0000:00:01.1/0000:02:00.0/nvme/nvme0/nvme0n1
+N: nvme0n1
+S: disk/by-id/lvm-pv-uuid-Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V
+E: DEVLINKS=/dev/disk/by-id/lvm-pv-uuid-Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V
+E: DEVNAME=/dev/nvme0n1
+E: DEVPATH=/devices/pci0000:00/0000:00:01.1/0000:02:00.0/nvme/nvme0/nvme0n1
+E: DEVTYPE=disk
+E: ID_FS_TYPE=LVM2_member
+E: ID_FS_USAGE=raid
+E: ID_FS_UUID=Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V
+E: ID_FS_UUID_ENC=Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V
+E: ID_FS_VERSION=LVM2 001
+E: MAJOR=259
+E: MINOR=0
+E: SUBSYSTEM=block
+E: TAGS=:systemd:
+E: USEC_INITIALIZED=3842
diff --git a/test/disk_tests/ssd_smart/disklist b/test/disk_tests/ssd_smart/disklist
new file mode 100644
index 0000000..9191c61
--- /dev/null
+++ b/test/disk_tests/ssd_smart/disklist
@@ -0,0 +1 @@
+sda
diff --git a/test/disk_tests/ssd_smart/disklist_expected.json b/test/disk_tests/ssd_smart/disklist_expected.json
new file mode 100644
index 0000000..3681cc8
--- /dev/null
+++ b/test/disk_tests/ssd_smart/disklist_expected.json
@@ -0,0 +1,16 @@
+{
+ "sda" : {
+ "serial" : "000000000000",
+ "vendor" : "ATA",
+ "rpm" : 0,
+ "gpt" : 1,
+ "health" : "PASSED",
+ "wearout" : "98",
+ "osdid" : -1,
+ "size" : 512000,
+ "type" : "ssd",
+ "devpath" : "/dev/sda",
+ "model" : "Samsung_SSD_860_EVO_1TB",
+ "wwn" : "0x0000000000000000"
+ }
+}
diff --git a/test/disk_tests/ssd_smart/sda/device/vendor b/test/disk_tests/ssd_smart/sda/device/vendor
new file mode 100644
index 0000000..531030d
--- /dev/null
+++ b/test/disk_tests/ssd_smart/sda/device/vendor
@@ -0,0 +1 @@
+ATA
diff --git a/test/disk_tests/ssd_smart/sda/queue/rotational b/test/disk_tests/ssd_smart/sda/queue/rotational
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/test/disk_tests/ssd_smart/sda/queue/rotational
@@ -0,0 +1 @@
+0
diff --git a/test/disk_tests/ssd_smart/sda/size b/test/disk_tests/ssd_smart/sda/size
new file mode 100644
index 0000000..83b33d2
--- /dev/null
+++ b/test/disk_tests/ssd_smart/sda/size
@@ -0,0 +1 @@
+1000
diff --git a/test/disk_tests/ssd_smart/sda_smart b/test/disk_tests/ssd_smart/sda_smart
new file mode 100644
index 0000000..907e7da
--- /dev/null
+++ b/test/disk_tests/ssd_smart/sda_smart
@@ -0,0 +1,352 @@
+{
+ "json_format_version": [
+ 1,
+ 0
+ ],
+ "smartctl": {
+ "version": [
+ 7,
+ 2
+ ],
+ "svn_revision": "5155",
+ "platform_info": "x86_64-linux-5.11.7-1-pve",
+ "build_info": "(local build)",
+ "argv": [
+ "smartctl",
+ "-j",
+ "-H",
+ "-Afbrief",
+ "/dev/sda"
+ ],
+ "exit_status": 0
+ },
+ "device": {
+ "name": "/dev/sda",
+ "info_name": "/dev/sda [SAT]",
+ "type": "sat",
+ "protocol": "ATA"
+ },
+ "smart_status": {
+ "passed": true
+ },
+ "ata_smart_attributes": {
+ "revision": 1,
+ "table": [
+ {
+ "id": 5,
+ "name": "Reallocated_Sector_Ct",
+ "value": 100,
+ "worst": 100,
+ "thresh": 10,
+ "when_failed": "",
+ "flags": {
+ "value": 51,
+ "string": "PO--CK ",
+ "prefailure": true,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 9,
+ "name": "Power_On_Hours",
+ "value": 99,
+ "worst": 99,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 2463,
+ "string": "2463"
+ }
+ },
+ {
+ "id": 12,
+ "name": "Power_Cycle_Count",
+ "value": 99,
+ "worst": 99,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 499,
+ "string": "499"
+ }
+ },
+ {
+ "id": 177,
+ "name": "Wear_Leveling_Count",
+ "value": 98,
+ "worst": 98,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 19,
+ "string": "PO--C- ",
+ "prefailure": true,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": false
+ },
+ "raw": {
+ "value": 20,
+ "string": "20"
+ }
+ },
+ {
+ "id": 179,
+ "name": "Used_Rsvd_Blk_Cnt_Tot",
+ "value": 100,
+ "worst": 100,
+ "thresh": 10,
+ "when_failed": "",
+ "flags": {
+ "value": 19,
+ "string": "PO--C- ",
+ "prefailure": true,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": false
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 181,
+ "name": "Program_Fail_Cnt_Total",
+ "value": 100,
+ "worst": 100,
+ "thresh": 10,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 182,
+ "name": "Erase_Fail_Count_Total",
+ "value": 100,
+ "worst": 100,
+ "thresh": 10,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 183,
+ "name": "Runtime_Bad_Block",
+ "value": 100,
+ "worst": 100,
+ "thresh": 10,
+ "when_failed": "",
+ "flags": {
+ "value": 19,
+ "string": "PO--C- ",
+ "prefailure": true,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": false
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 187,
+ "name": "Uncorrectable_Error_Cnt",
+ "value": 100,
+ "worst": 100,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 190,
+ "name": "Airflow_Temperature_Cel",
+ "value": 73,
+ "worst": 45,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 27,
+ "string": "27"
+ }
+ },
+ {
+ "id": 195,
+ "name": "ECC_Error_Rate",
+ "value": 200,
+ "worst": 200,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 26,
+ "string": "-O-RC- ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": true,
+ "event_count": true,
+ "auto_keep": false
+ },
+ "raw": {
+ "value": 0,
+ "string": "0"
+ }
+ },
+ {
+ "id": 199,
+ "name": "CRC_Error_Count",
+ "value": 99,
+ "worst": 99,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 62,
+ "string": "-OSRCK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": true,
+ "error_rate": true,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 1,
+ "string": "1"
+ }
+ },
+ {
+ "id": 235,
+ "name": "POR_Recovery_Count",
+ "value": 99,
+ "worst": 99,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 18,
+ "string": "-O--C- ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": false
+ },
+ "raw": {
+ "value": 39,
+ "string": "39"
+ }
+ },
+ {
+ "id": 241,
+ "name": "Total_LBAs_Written",
+ "value": 99,
+ "worst": 99,
+ "thresh": 0,
+ "when_failed": "",
+ "flags": {
+ "value": 50,
+ "string": "-O--CK ",
+ "prefailure": false,
+ "updated_online": true,
+ "performance": false,
+ "error_rate": false,
+ "event_count": true,
+ "auto_keep": true
+ },
+ "raw": {
+ "value": 19999585737,
+ "string": "19999585737"
+ }
+ }
+ ]
+ },
+ "power_on_time": {
+ "hours": 2515
+ },
+ "power_cycle_count": 508,
+ "temperature": {
+ "current": 29
+ }
+}
diff --git a/test/disk_tests/ssd_smart/sda_smart_expected.json b/test/disk_tests/ssd_smart/sda_smart_expected.json
new file mode 100644
index 0000000..36da71e
--- /dev/null
+++ b/test/disk_tests/ssd_smart/sda_smart_expected.json
@@ -0,0 +1,146 @@
+{
+ "attributes" : [
+ {
+ "fail" : "-",
+ "flags" : "PO--CK",
+ "id" : 5,
+ "name" : "Reallocated_Sector_Ct",
+ "raw" : "0",
+ "threshold" : 10,
+ "value" : 100,
+ "worst" : 100
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 9,
+ "name" : "Power_On_Hours",
+ "raw" : "2463",
+ "threshold" : 0,
+ "value" : 99,
+ "worst" : 99
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 12,
+ "name" : "Power_Cycle_Count",
+ "raw" : "499",
+ "threshold" : 0,
+ "value" : 99,
+ "worst" : 99
+ },
+ {
+ "fail" : "-",
+ "flags" : "PO--C-",
+ "id" : 177,
+ "name" : "Wear_Leveling_Count",
+ "raw" : "20",
+ "threshold" : 0,
+ "value" : 98,
+ "worst" : 98
+ },
+ {
+ "fail" : "-",
+ "flags" : "PO--C-",
+ "id" : 179,
+ "name" : "Used_Rsvd_Blk_Cnt_Tot",
+ "raw" : "0",
+ "threshold" : 10,
+ "value" : 100,
+ "worst" : 100
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 181,
+ "name" : "Program_Fail_Cnt_Total",
+ "raw" : "0",
+ "threshold" : 10,
+ "value" : 100,
+ "worst" : 100
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 182,
+ "name" : "Erase_Fail_Count_Total",
+ "raw" : "0",
+ "threshold" : 10,
+ "value" : 100,
+ "worst" : 100
+ },
+ {
+ "fail" : "-",
+ "flags" : "PO--C-",
+ "id" : 183,
+ "name" : "Runtime_Bad_Block",
+ "raw" : "0",
+ "threshold" : 10,
+ "value" : 100,
+ "worst" : 100
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 187,
+ "name" : "Uncorrectable_Error_Cnt",
+ "raw" : "0",
+ "threshold" : 0,
+ "value" : 100,
+ "worst" : 100
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 190,
+ "name" : "Airflow_Temperature_Cel",
+ "raw" : "27",
+ "threshold" : 0,
+ "value" : 73,
+ "worst" : 45
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O-RC-",
+ "id" : 195,
+ "name" : "ECC_Error_Rate",
+ "raw" : "0",
+ "threshold" : 0,
+ "value" : 200,
+ "worst" : 200
+ },
+ {
+ "fail" : "-",
+ "flags" : "-OSRCK",
+ "id" : 199,
+ "name" : "CRC_Error_Count",
+ "raw" : "1",
+ "threshold" : 0,
+ "value" : 99,
+ "worst" : 99
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--C-",
+ "id" : 235,
+ "name" : "POR_Recovery_Count",
+ "raw" : "39",
+ "threshold" : 0,
+ "value" : 99,
+ "worst" : 99
+ },
+ {
+ "fail" : "-",
+ "flags" : "-O--CK",
+ "id" : 241,
+ "name" : "Total_LBAs_Written",
+ "raw" : "19999585737",
+ "threshold" : 0,
+ "value" : 99,
+ "worst" : 99
+ }
+ ],
+ "health" : "PASSED",
+ "type" : "ata"
+}
diff --git a/test/disk_tests/ssd_smart/sda_udevadm b/test/disk_tests/ssd_smart/sda_udevadm
new file mode 100644
index 0000000..f662221
--- /dev/null
+++ b/test/disk_tests/ssd_smart/sda_udevadm
@@ -0,0 +1,11 @@
+E: DEVNAME=/dev/sda
+E: DEVTYPE=disk
+E: ID_ATA_ROTATION_RATE_RPM=0
+E: ID_BUS=ata
+E: ID_MODEL=Samsung_SSD_860_EVO_1TB
+E: ID_PART_TABLE_TYPE=gpt
+E: ID_SERIAL=Samsung_SSD_860_EVO_1TB_000000000000000
+E: ID_SERIAL_SHORT=000000000000
+E: ID_TYPE=disk
+E: ID_WWN=0x0000000000000000
+
diff --git a/test/disklist_test.pm b/test/disklist_test.pm
index 7f0e0be..727fb44 100644
--- a/test/disklist_test.pm
+++ b/test/disklist_test.pm
@@ -34,10 +34,10 @@ sub mocked_run_command {
my $dev;
my $type;
if (@$cmd > 3) {
- $dev = $cmd->[5];
+ $dev = $cmd->[4];
$type = 'smart';
} else {
- $dev = $cmd->[2];
+ $dev = $cmd->[3];
$type = 'health';
}
$dev =~ s|/dev/||;
--
2.20.1
More information about the pve-devel
mailing list