From 1b7264793e39bbdfa8f07ec552635cf75167aa58 Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Wed, 24 Apr 2024 15:51:01 +0400 Subject: [PATCH] ATA JSON parser improvements. --- src/applib/smartctl_json_ata_parser.cpp | 94 +++++++++++++++++++++-- src/applib/smartctl_json_ata_parser.h | 4 - src/applib/smartctl_json_basic_parser.cpp | 4 +- src/applib/smartctl_text_ata_parser.cpp | 10 +-- src/applib/smartctl_text_basic_parser.cpp | 4 +- src/applib/smartctl_version_parser.h | 4 +- src/applib/storage_device.cpp | 4 +- 7 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/applib/smartctl_json_ata_parser.cpp b/src/applib/smartctl_json_ata_parser.cpp index 64a6147..6af48ad 100644 --- a/src/applib/smartctl_json_ata_parser.cpp +++ b/src/applib/smartctl_json_ata_parser.cpp @@ -62,8 +62,8 @@ Information not printed in JSON yet: We ignore this in text parser. - SMART support and some other Info keys - _text_only/smart_supported - _text_only/smart_enabled + smart_support/available + smart_support/enabled _text_only/write_cache_reorder _text_only/power_mode @@ -321,6 +321,46 @@ hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_info( }, {"local_time/asctime", _("Scanned on"), string_formatter()}, + + {"smart_support/available", _("SMART Supported"), bool_formatter(_("Yes"), _("No"))}, + {"smart_support/enabled", _("SMART Enabled"), bool_formatter(_("Yes"), _("No"))}, + + {"ata_aam/enabled", _("AAM Feature"), bool_formatter(_("Enabled"), _("Disabled"))}, + {"ata_aam/level", _("AAM Level"), + [](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name) + -> hz::ExpectedValue + { + int64_t level = get_node_data(root_node, "ata_aam/level").value_or(0); + std::string level_string = get_node_data(root_node, "ata_aam/string").value_or(""); + AtaStorageProperty p; + p.set_name(key, key, displayable_name); + p.readable_value = std::format("{} ({})", level_string, level); + p.value = level; + return p; + } + }, + {"ata_aam/recommended_level", _("AAM Recommended Level"), + custom_string_formatter([](int64_t value) + { + return std::format("{}", value); + }) + }, + + {"ata_apm/enabled", _("APM Feature"), bool_formatter(_("Enabled"), _("Disabled"))}, + {"ata_apm/level", _("APM Level"), + [](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name) + -> hz::ExpectedValue + { + int64_t level = get_node_data(root_node, "ata_apm/level").value_or(0); + std::string level_string = get_node_data(root_node, "ata_apm/string").value_or(""); + AtaStorageProperty p; + p.set_name(key, key, displayable_name); + p.readable_value = std::format("{} ({})", level_string, level); + p.value = level; + return p; + } + }, + {"read_lookahead/enabled", _("Read Look-Ahead"), bool_formatter(_("Enabled"), _("Disabled"))}, {"write_cache/enabled", _("Write Cache"), bool_formatter(_("Enabled"), _("Disabled"))}, {"ata_dsn/enabled", _("DSN Feature"), bool_formatter(_("Enabled"), _("Disabled"))}, @@ -1200,14 +1240,54 @@ hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_devst hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_sataphy(const nlohmann::json& json_root_node) { - return hz::ExpectedVoid(); -} + using namespace SmartctlJsonParserHelpers; + using namespace std::string_literals; + bool section_properties_found = false; + std::vector lines; -hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_internal_capabilities(AtaStorageProperty& cap_prop) -{ - return hz::ExpectedVoid(); + // Table + const std::string table_key = "sata_phy_event_counters/table"; + auto table_node = get_node(json_root_node, table_key); + + // Entries + if (table_node.has_value() && table_node->is_array()) { + for (const auto& table_entry : table_node.value()) { + const uint64_t id = get_node_data(table_entry, "id").value_or(0); + const std::string name = get_node_data(table_entry, "name").value_or(std::string()); + const uint64_t size = get_node_data(table_entry, "size").value_or(0); + const int64_t value = get_node_data(table_entry, "value").value_or(0); +// const bool overflow = get_node_data(table_entry, "overflow").value_or(false); + + lines.emplace_back(std::format( + "ID: 0x{:02X} Size: {:8} Value: {:20} Description: {}", + id, + size, + value, + name)); + } + + // The whole section + { + AtaStorageProperty p; + p.set_name("SATA Phy Log", "sata_phy_event_counters/_merged"); + p.section = AtaStorageProperty::Section::PhyLog; + p.reported_value = hz::string_join(lines, "\n"); + p.value = p.reported_value; // string-type value + + add_property(p); + } + + section_properties_found = true; + } + + if (!section_properties_found) { + return hz::Unexpected(SmartctlParserError::NoSection, + std::format("No section {} parsed.", AtaStorageProperty::get_readable_section_name(AtaStorageProperty::Section::PhyLog))); + } + + return {}; } diff --git a/src/applib/smartctl_json_ata_parser.h b/src/applib/smartctl_json_ata_parser.h index d2e51b5..9fa0db0 100644 --- a/src/applib/smartctl_json_ata_parser.h +++ b/src/applib/smartctl_json_ata_parser.h @@ -72,10 +72,6 @@ class SmartctlJsonAtaParser : public SmartctlParser { hz::ExpectedVoid parse_section_sataphy(const nlohmann::json& json_root_node); - /// Check the capabilities for internal properties we can use. - hz::ExpectedVoid parse_section_internal_capabilities(AtaStorageProperty& cap_prop); - - }; diff --git a/src/applib/smartctl_json_basic_parser.cpp b/src/applib/smartctl_json_basic_parser.cpp index 59234e7..dc8e05f 100644 --- a/src/applib/smartctl_json_basic_parser.cpp +++ b/src/applib/smartctl_json_basic_parser.cpp @@ -79,14 +79,14 @@ hz::ExpectedVoid SmartctlJsonBasicParser::parse_section_bas { AtaStorageProperty p; - p.set_name("SMART Supported", "_text_only/smart_supported", "SMART Supported"); + p.set_name("SMART Supported", "smart_support/available", "SMART Supported"); p.value = smart_supported; p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } { AtaStorageProperty p; - p.set_name("SMART Enabled", "_text_only/smart_enabled", "SMART Enabled"); + p.set_name("SMART Enabled", "smart_support/enabled", "SMART Enabled"); p.value = smart_enabled; p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); diff --git a/src/applib/smartctl_text_ata_parser.cpp b/src/applib/smartctl_text_ata_parser.cpp index 38b6850..9093dac 100644 --- a/src/applib/smartctl_text_ata_parser.cpp +++ b/src/applib/smartctl_text_ata_parser.cpp @@ -532,25 +532,25 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_info_ // Don't put complete messages here - they change across smartctl versions. if (app_pcre_match("/Available - device has/mi", p.reported_value)) { - p.set_name(p.reported_name, "_text_only/smart_supported", "SMART Supported"); + p.set_name(p.reported_name, "smart_support/available", "SMART Supported"); p.value = true; } else if (app_pcre_match("/Enabled/mi", p.reported_value)) { - p.set_name(p.reported_name, "_text_only/smart_enabled", "SMART Enabled"); + p.set_name(p.reported_name, "smart_support/enabled", "SMART Enabled"); p.value = true; } else if (app_pcre_match("/Disabled/mi", p.reported_value)) { - p.set_name(p.reported_name, "_text_only/smart_enabled", "SMART Enabled"); + p.set_name(p.reported_name, "smart_support/enabled", "SMART Enabled"); p.value = false; } else if (app_pcre_match("/Unavailable/mi", p.reported_value)) { - p.set_name(p.reported_name, "_text_only/smart_supported", "SMART Supported"); + p.set_name(p.reported_name, "smart_support/available", "SMART Supported"); p.value = false; // this should be the last - when ambiguous state is detected, usually smartctl // retries with other methods and prints one of the above. } else if (app_pcre_match("/Ambiguous/mi", p.reported_value)) { - p.set_name(p.reported_name, "_text_only/smart_supported", "SMART Supported"); + p.set_name(p.reported_name, "smart_support/available", "SMART Supported"); p.value = true; // let's be optimistic - just hope that it doesn't hurt. } diff --git a/src/applib/smartctl_text_basic_parser.cpp b/src/applib/smartctl_text_basic_parser.cpp index 091c436..b536cc0 100644 --- a/src/applib/smartctl_text_basic_parser.cpp +++ b/src/applib/smartctl_text_basic_parser.cpp @@ -132,14 +132,14 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string { AtaStorageProperty p; - p.set_name("SMART Supported", "_text_only/smart_supported", "SMART Supported"); + p.set_name("SMART Supported", "smart_support/available", "SMART Supported"); p.value = smart_supported; p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } { AtaStorageProperty p; - p.set_name("SMART Enabled", "_text_only/smart_enabled", "SMART Enabled"); + p.set_name("SMART Enabled", "smart_support/enabled", "SMART Enabled"); p.value = smart_enabled; p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); diff --git a/src/applib/smartctl_version_parser.h b/src/applib/smartctl_version_parser.h index 681654c..93ef054 100644 --- a/src/applib/smartctl_version_parser.h +++ b/src/applib/smartctl_version_parser.h @@ -57,8 +57,8 @@ class SmartctlVersionParser { // Can't fully test 5.0-xx, they don't support sata, and I have only sata. static constexpr double minimum_req_text_version = 5.0; - // JSON in 7.2 is still experimental, but we have to have some cut-off point. - static constexpr double minimum_req_json_version = 7.2; + // 7.3 adds "smart_support" section in json. + static constexpr double minimum_req_json_version = 7.3; }; diff --git a/src/applib/storage_device.cpp b/src/applib/storage_device.cpp index a47a8d6..b53d9ff 100644 --- a/src/applib/storage_device.cpp +++ b/src/applib/storage_device.cpp @@ -188,10 +188,10 @@ hz::ExpectedVoid StorageDevice::parse_basic_data(bool do_set } } - if (auto prop = basic_property_repo.lookup_property("_text_only/smart_supported"); !prop.empty()) { + if (auto prop = basic_property_repo.lookup_property("smart_support/available"); !prop.empty()) { smart_supported_ = prop.get_value(); } - if (auto prop = basic_property_repo.lookup_property("_text_only/smart_enabled"); !prop.empty()) { + if (auto prop = basic_property_repo.lookup_property("smart_support/enabled"); !prop.empty()) { smart_enabled_ = prop.get_value(); } if (auto prop = basic_property_repo.lookup_property("model_name"); !prop.empty()) {