mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-26 05:45:35 +00:00
Support parsing ATA Capabilities from JSON.
This commit is contained in:
@@ -248,6 +248,29 @@ class AtaStorageProperty {
|
||||
[[nodiscard]] static std::string get_readable_section_name(Section s);
|
||||
|
||||
|
||||
using ValueVariantType = std::variant<
|
||||
std::monostate, ///< None
|
||||
std::string, ///< Value (if it's a string)
|
||||
int64_t, ///< Value (if it's an integer)
|
||||
bool, ///< Value (if it's bool)
|
||||
std::chrono::seconds, ///< Value in seconds (if it's time interval)
|
||||
AtaStorageCapability, ///< Value (if it's a capability)
|
||||
AtaStorageAttribute, ///< Value (if it's an attribute)
|
||||
AtaStorageStatistic, ///< Value (if it's a statistic from devstat)
|
||||
AtaStorageErrorBlock, ///< Value (if it's a error block)
|
||||
AtaStorageSelftestEntry ///< Value (if it's a self-test entry)
|
||||
>;
|
||||
|
||||
|
||||
/// Constructor
|
||||
AtaStorageProperty() = default;
|
||||
|
||||
/// Constructor
|
||||
AtaStorageProperty(Section section_, ValueVariantType value_)
|
||||
: section(section_), value(std::move(value_))
|
||||
{ }
|
||||
|
||||
|
||||
/// Get displayable value type name
|
||||
[[nodiscard]] std::string get_storable_value_type_name() const;
|
||||
|
||||
@@ -297,18 +320,7 @@ class AtaStorageProperty {
|
||||
std::string reported_value; ///< String representation of the value as reported
|
||||
std::string readable_value; ///< User-friendly readable representation of value. if empty, use the other members.
|
||||
|
||||
std::variant<
|
||||
std::monostate, ///< None
|
||||
std::string, ///< Value (if it's a string)
|
||||
int64_t, ///< Value (if it's an integer)
|
||||
bool, ///< Value (if it's bool)
|
||||
std::chrono::seconds, ///< Value in seconds (if it's time interval)
|
||||
AtaStorageCapability, ///< Value (if it's a capability)
|
||||
AtaStorageAttribute, ///< Value (if it's an attribute)
|
||||
AtaStorageStatistic, ///< Value (if it's a statistic from devstat)
|
||||
AtaStorageErrorBlock, ///< Value (if it's a error block)
|
||||
AtaStorageSelftestEntry ///< Value (if it's a self-test entry)
|
||||
> value;
|
||||
ValueVariantType value; ///< Stored value
|
||||
|
||||
WarningLevel warning_level = WarningLevel::None; ///< Warning severity for this property
|
||||
std::string warning_reason; // Warning reason (displayable)
|
||||
|
||||
@@ -1613,7 +1613,7 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA
|
||||
|| auto_set(p, "ata_smart_data/self_test/status/_group", "Status of the last self-test run.")
|
||||
|| auto_set(p, "ata_smart_data/offline_data_collection/_group", "Drive properties related to Offline Data Collection and self-tests.")
|
||||
|| auto_set(p, "ata_smart_data/capabilities/_group", "Drive properties related to SMART handling.")
|
||||
|| auto_set(p, "ata_smart_data/capabilities/error_logging_supported", "Drive properties related to error logging.")
|
||||
|| auto_set(p, "ata_smart_data/capabilities/error_logging_supported/_group", "Drive properties related to error logging.")
|
||||
|| auto_set(p, "ata_sct_capabilities/_group", "Drive properties related to temperature information.");
|
||||
break;
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ hz::ExpectedVoid<SelfTestError> SelfTest::update(const std::shared_ptr<CommandEx
|
||||
for (const auto& e : property_repo.get_properties()) {
|
||||
if (e.section != AtaStorageProperty::Section::Internal
|
||||
|| !e.is_value_type<AtaStorageSelftestEntry>() || e.get_value<AtaStorageSelftestEntry>().test_num != 0
|
||||
|| e.generic_name != "ata_smart_data/self_test/status/passed")
|
||||
|| e.generic_name != "ata_smart_data/self_test/status/_merged")
|
||||
continue;
|
||||
p = e;
|
||||
}
|
||||
|
||||
@@ -9,15 +9,23 @@ Copyright:
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "smartctl_json_ata_parser.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "ata_storage_property.h"
|
||||
#include "hz/debug.h"
|
||||
#include "hz/string_algo.h"
|
||||
// #include "smartctl_version_parser.h"
|
||||
#include "hz/format_unit.h"
|
||||
#include "hz/error_container.h"
|
||||
#include "hz/string_num.h"
|
||||
#include "smartctl_json_parser_helpers.h"
|
||||
|
||||
|
||||
@@ -103,8 +111,24 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse(std::string_v
|
||||
add_property(merged_property);
|
||||
add_property(full_property);
|
||||
|
||||
// Info must be supported.
|
||||
auto info_parse_status = parse_section_info(json_root_node);
|
||||
auto health_parse_status = parse_section_health(json_root_node);
|
||||
if (!info_parse_status) {
|
||||
return info_parse_status;
|
||||
}
|
||||
|
||||
// Ignore parse errors here, they are not critical.
|
||||
[[maybe_unused]] auto health_parse_status = parse_section_health(json_root_node);
|
||||
[[maybe_unused]] auto capabilities_parse_status = parse_section_capabilities(json_root_node);
|
||||
[[maybe_unused]] auto attributes_parse_status = parse_section_attributes(json_root_node);
|
||||
[[maybe_unused]] auto directory_log_parse_status = parse_section_directory_log(json_root_node);
|
||||
[[maybe_unused]] auto error_log_parse_status = parse_section_error_log(json_root_node);
|
||||
[[maybe_unused]] auto selftest_log_parse_status = parse_section_selftest_log(json_root_node);
|
||||
[[maybe_unused]] auto selective_selftest_log_parse_status = parse_section_selective_selftest_log(json_root_node);
|
||||
[[maybe_unused]] auto scttemp_log_parse_status = parse_section_scttemp_log(json_root_node);
|
||||
[[maybe_unused]] auto scterc_log_parse_status = parse_section_scterc_log(json_root_node);
|
||||
[[maybe_unused]] auto devstat_parse_status = parse_section_devstat(json_root_node);
|
||||
[[maybe_unused]] auto sataphy_parse_status = parse_section_sataphy(json_root_node);
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -115,7 +139,10 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(
|
||||
{
|
||||
using namespace SmartctlJsonParserHelpers;
|
||||
|
||||
static const std::vector<std::tuple<std::string, std::string, PropertyRetrievalFunc>> info_keys = {
|
||||
// This is very similar to Basic Parser, but the Basic Parser supports different drive types, while this
|
||||
// one is only for ATA.
|
||||
|
||||
static const std::vector<std::tuple<std::string, std::string, PropertyRetrievalFunc>> json_keys = {
|
||||
{"model_family", _("Model Family"), string_formatter()},
|
||||
{"model_name", _("Device Model"), string_formatter()},
|
||||
{"serial_number", _("Serial Number"), string_formatter()},
|
||||
@@ -216,16 +243,21 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(
|
||||
{"ata_security/string", _("ATA Security"), string_formatter()},
|
||||
};
|
||||
|
||||
for (const auto& [key, displayable_name, retrieval_func] : info_keys) {
|
||||
bool any_found = false;
|
||||
for (const auto& [key, displayable_name, retrieval_func] : json_keys) {
|
||||
DBG_ASSERT(retrieval_func != nullptr);
|
||||
|
||||
auto p = retrieval_func(json_root_node, key, displayable_name);
|
||||
if (p.has_value()) { // ignore if not found
|
||||
p->section = AtaStorageProperty::Section::Info;
|
||||
add_property(p.value());
|
||||
any_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_found) {
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, "No keys info found in JSON data.");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -254,4 +286,300 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_healt
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_capabilities(const nlohmann::json& json_root_node)
|
||||
{
|
||||
using namespace SmartctlJsonParserHelpers;
|
||||
|
||||
static const std::vector<std::tuple<std::string, std::string, PropertyRetrievalFunc>> json_keys = {
|
||||
// // FIXME Remove?
|
||||
// {"ata_smart_data/capabilities/_group", _("SMART Capabilities"),
|
||||
// conditional_formatter("ata_smart_data/capabilities/values",
|
||||
// AtaStorageProperty{} )},
|
||||
//
|
||||
// // FIXME Remove?
|
||||
// {"ata_smart_data/capabilities/error_logging_supported/_group", _("Error Logging Capabilities"),
|
||||
// conditional_formatter("ata_smart_data/capabilities/error_logging_supported",
|
||||
// AtaStorageProperty{} )},
|
||||
//
|
||||
|
||||
{"ata_smart_data/offline_data_collection/status/_auto_enabled", _("Automatic offline data collection status"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_data<int64_t>(root_node, "ata_smart_data/offline_data_collection/status/value");
|
||||
if (value_val.has_value()) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = static_cast<bool>(value_val.value() & 0x80); // taken from ataprint.cpp
|
||||
p.readable_value = p.get_value<bool>() ? _("Enabled") : _("Disabled");
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Last self-test status
|
||||
{"ata_smart_data/offline_data_collection/status/value/_decoded", "Last offline data collection status",
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_data<uint8_t>(root_node, "ata_smart_data/offline_data_collection/status/value");
|
||||
if (value_val.has_value()) {
|
||||
std::string status_str;
|
||||
switch (value_val.value() & 0x7f) {
|
||||
// Data from smartmontools/ataprint.cpp
|
||||
case 0x00: status_str = _("Never started"); break;
|
||||
case 0x02: status_str = _("Completed without error"); break;
|
||||
case 0x03: status_str = (value_val.value() == 0x03 ? _("In progress") : _("In reserved state")); break;
|
||||
case 0x04: status_str = _("Suspended by an interrupting command from host"); break;
|
||||
case 0x05: status_str = _("Aborted by an interrupting command from host"); break;
|
||||
case 0x06: status_str = _("Aborted by the device with a fatal error"); break;
|
||||
default: status_str = ((value_val.value() & 0x7f) > 0x40 ? _("In vendor-specific state") : _("In reserved state")); break;
|
||||
}
|
||||
AtaStorageProperty p;
|
||||
p.section = AtaStorageProperty::Section::Capabilities;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = status_str;
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"ata_smart_data/offline_data_collection/completion_seconds", _("Time to complete offline data collection"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_data<int64_t>(root_node, key);
|
||||
if (value_val.has_value()) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = std::chrono::seconds(value_val.value());
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"ata_smart_data/self_test/status/value/_decoded", _("Self-test execution status"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
// Testing:
|
||||
// "status": {
|
||||
// "value": 249,
|
||||
// "string": "in progress, 90% remaining",
|
||||
// "remaining_percent": 90
|
||||
// },
|
||||
|
||||
// Not testing:
|
||||
// "status": {
|
||||
// "value": 0,
|
||||
// "string": "completed without error",
|
||||
// "passed": true
|
||||
// },
|
||||
|
||||
AtaStorageSelftestEntry::Status status = AtaStorageSelftestEntry::Status::Unknown;
|
||||
|
||||
auto value_val = get_node_data<uint8_t>(root_node, "ata_smart_data/self_test/status/value");
|
||||
if (value_val.has_value()) {
|
||||
switch (value_val.value() >> 4) {
|
||||
// Data from smartmontools/ataprint.cpp
|
||||
case 0x0: status = AtaStorageSelftestEntry::Status::CompletedNoError; break;
|
||||
case 0x1: status = AtaStorageSelftestEntry::Status::AbortedByHost; break;
|
||||
case 0x2: status = AtaStorageSelftestEntry::Status::Interrupted; break;
|
||||
case 0x3: status = AtaStorageSelftestEntry::Status::FatalOrUnknown; break;
|
||||
case 0x4: status = AtaStorageSelftestEntry::Status::ComplUnknownFailure; break;
|
||||
case 0x5: status = AtaStorageSelftestEntry::Status::ComplElectricalFailure; break;
|
||||
case 0x6: status = AtaStorageSelftestEntry::Status::ComplServoFailure; break;
|
||||
case 0x7: status = AtaStorageSelftestEntry::Status::ComplReadFailure; break;
|
||||
case 0x8: status = AtaStorageSelftestEntry::Status::ComplHandlingDamage; break;
|
||||
// Special case
|
||||
case 0xf: status = AtaStorageSelftestEntry::Status::InProgress; break;
|
||||
default: status = AtaStorageSelftestEntry::Status::Reserved; break;
|
||||
}
|
||||
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = AtaStorageSelftestEntry::get_readable_status_name(status);
|
||||
return p;
|
||||
}
|
||||
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"ata_smart_data/self_test/status/remaining_percent", _("Self-test remaining percentage"),
|
||||
custom_string_formatter<int64_t>([](int64_t value)
|
||||
{
|
||||
return std::format("{} %", value);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
{"ata_smart_data/capabilities/self_tests_supported", _("Self-tests supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
|
||||
{"ata_smart_data/capabilities/exec_offline_immediate_supported", _("Offline immediate test supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
{"ata_smart_data/capabilities/offline_is_aborted_upon_new_cmd", _("Abort offline collection on new command"), bool_formatter(_("Yes"), _("No"))},
|
||||
{"ata_smart_data/capabilities/offline_surface_scan_supported", _("Offline surface scan supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
|
||||
{"ata_smart_data/capabilities/conveyance_self_test_supported", _("Conveyance self-test supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
{"ata_smart_data/capabilities/selective_self_test_supported", _("Selective self-test supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
|
||||
{"ata_smart_data/self_test/polling_minutes/short", _("Short self-test status recommended polling time"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_data<int64_t>(root_node, key);
|
||||
if (value_val.has_value()) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = std::chrono::minutes(value_val.value());
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"ata_smart_data/self_test/polling_minutes/extended", _("Extended self-test status recommended polling time"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_data<int64_t>(root_node, key);
|
||||
if (value_val.has_value()) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = std::chrono::minutes(value_val.value());
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"ata_smart_data/self_test/polling_minutes/conveyance", _("Conveyance self-test status recommended polling time"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_data<int64_t>(root_node, key);
|
||||
if (value_val.has_value()) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = std::chrono::minutes(value_val.value());
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{"ata_smart_data/capabilities/attribute_autosave_enabled", _("Saves SMART data before entering power-saving mode"), bool_formatter(_("Enabled"), _("Disabled"))},
|
||||
|
||||
{"ata_smart_data/capabilities/error_logging_supported", _("Error logging supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
{"ata_smart_data/capabilities/gp_logging_supported", _("General purpose logging supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
|
||||
{"ata_sct_capabilities/_supported", _("SCT capabilities supported"),
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto value_val = get_node_exists(root_node, "ata_sct_capabilities");
|
||||
if (value_val.has_value()) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.value = value_val.value();
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
{"ata_sct_capabilities/error_recovery_control_supported", _("SCT error recovery control supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
{"ata_sct_capabilities/feature_control_supported", _("SCT feature control supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
{"ata_sct_capabilities/data_table_supported", _("SCT data table supported"), bool_formatter(_("Yes"), _("No"))},
|
||||
|
||||
};
|
||||
|
||||
for (const auto& [key, displayable_name, retrieval_func] : json_keys) {
|
||||
DBG_ASSERT(retrieval_func != nullptr);
|
||||
auto p = retrieval_func(json_root_node, key, displayable_name);
|
||||
if (p.has_value()) { // ignore if not found
|
||||
p->section = AtaStorageProperty::Section::Capabilities;
|
||||
add_property(p.value());
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_attributes(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_directory_log(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_error_log(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_selftest_log(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_selective_selftest_log(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_scttemp_log(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_scterc_log(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_devstat(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_sataphy(const nlohmann::json& json_root_node)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_internal_capabilities(AtaStorageProperty& cap_prop)
|
||||
{
|
||||
return hz::ExpectedVoid<SmartctlParserError>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -12,10 +12,14 @@ Copyright:
|
||||
#ifndef SMARTCTL_JSON_ATA_PARSER_H
|
||||
#define SMARTCTL_JSON_ATA_PARSER_H
|
||||
|
||||
#include "smartctl_parser.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "json/json.hpp"
|
||||
#include "smartctl_parser.h"
|
||||
|
||||
#include "hz/error_container.h"
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +40,40 @@ class SmartctlJsonAtaParser : public SmartctlParser {
|
||||
|
||||
/// Parse the health section (root node), filling in the properties
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_health(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_capabilities(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_attributes(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_directory_log(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_error_log(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_selftest_log(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_selective_selftest_log(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_scttemp_log(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_scterc_log(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_devstat(const nlohmann::json& json_root_node);
|
||||
|
||||
/// Parse a section from json data
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_sataphy(const nlohmann::json& json_root_node);
|
||||
|
||||
|
||||
/// Check the capabilities for internal properties we can use.
|
||||
hz::ExpectedVoid<SmartctlParserError> parse_section_internal_capabilities(AtaStorageProperty& cap_prop);
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -9,22 +9,30 @@ Copyright:
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include "smartctl_json_basic_parser.h"
|
||||
|
||||
// #include "local_glibmm.h"
|
||||
//#include <clocale> // localeconv
|
||||
//#include <cstdint>
|
||||
//#include <utility>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
|
||||
//#include "hz/string_algo.h" // string_*
|
||||
//#include "hz/string_num.h" // string_is_numeric, number_to_string
|
||||
#include "hz/string_algo.h" // string_*
|
||||
#include "hz/string_num.h" // string_is_numeric, number_to_string
|
||||
//#include "hz/debug.h" // debug_*
|
||||
#include "json/json.hpp"
|
||||
#include "hz/error_container.h"
|
||||
|
||||
//#include "app_pcrecpp.h"
|
||||
//#include "smartctl_text_ata_parser.h"
|
||||
//#include "ata_storage_property_descr.h"
|
||||
#include "ata_storage_property.h"
|
||||
#include "smartctl_json_parser_helpers.h"
|
||||
#include "smartctl_parser_types.h"
|
||||
//#include "smartctl_version_parser.h"
|
||||
#include "smartctl_json_basic_parser.h"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ Copyright:
|
||||
//#include <string>
|
||||
//#include <vector>
|
||||
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "smartctl_parser.h"
|
||||
|
||||
|
||||
|
||||
@@ -12,12 +12,17 @@ Copyright:
|
||||
#ifndef SMARTCTL_JSON_PARSER_HELPERS_H
|
||||
#define SMARTCTL_JSON_PARSER_HELPERS_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "json/json.hpp"
|
||||
#include "hz/debug.h"
|
||||
#include "hz/string_algo.h"
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
#include "hz/format_unit.h"
|
||||
#include "hz/error_container.h"
|
||||
@@ -38,10 +43,9 @@ enum class SmartctlJsonParserError {
|
||||
namespace SmartctlJsonParserHelpers {
|
||||
|
||||
|
||||
/// Get json node data. The path is slash-separated string.
|
||||
/// \throws std::runtime_error If not found or one of the paths is not an object
|
||||
template<typename T>
|
||||
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, const std::string& path)
|
||||
/// Get node from json data. The path is slash-separated string.
|
||||
[[nodiscard]] inline hz::ExpectedValue<nlohmann::json, SmartctlJsonParserError>
|
||||
get_node(const nlohmann::json& root, std::string_view path)
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -63,13 +67,7 @@ template<typename T>
|
||||
if (auto iter = curr->find(comp_name); iter != curr->end()) { // path component exists
|
||||
const auto& jval = iter.value();
|
||||
if (comp_index + 1 == components.size()) { // it's the "value" component
|
||||
try {
|
||||
return jval.get<T>(); // may throw json::type_error
|
||||
}
|
||||
catch (nlohmann::json::type_error& ex) {
|
||||
return hz::Unexpected(SmartctlJsonParserError::TypeError,
|
||||
std::format("Cannot get node data \"{}\", component \"{}\" has wrong type: {}.", path, comp_name, ex.what()));
|
||||
}
|
||||
return jval;
|
||||
}
|
||||
// continue to the next component
|
||||
curr = &jval;
|
||||
@@ -85,10 +83,32 @@ template<typename T>
|
||||
|
||||
|
||||
|
||||
|
||||
/// Get json node data. The path is slash-separated string.
|
||||
/// \return SmartctlJsonParserError on error.
|
||||
template<typename T>
|
||||
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, std::string_view path)
|
||||
{
|
||||
auto node_result = get_node(root, path);
|
||||
if (!node_result) {
|
||||
return hz::UnexpectedFrom(node_result);
|
||||
}
|
||||
|
||||
try {
|
||||
return node_result.value().get<T>(); // may throw json::type_error
|
||||
}
|
||||
catch (nlohmann::json::type_error& ex) {
|
||||
return hz::Unexpected(SmartctlJsonParserError::TypeError,
|
||||
std::format("Cannot get node data \"{}\", component has wrong type: {}.", path, ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get json node data. The path is slash-separated string.
|
||||
/// If the data is not is found, the default value is returned.
|
||||
template<typename T>
|
||||
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, const std::string& path, const T& default_value)
|
||||
[[nodiscard]] hz::ExpectedValue<T, SmartctlJsonParserError> get_node_data(const nlohmann::json& root, std::string_view path, const T& default_value)
|
||||
{
|
||||
auto expected_data = get_node_data<T>(root, path);
|
||||
|
||||
@@ -110,6 +130,31 @@ template<typename T>
|
||||
|
||||
|
||||
|
||||
/// Check if json node exists. The path is slash-separated string.
|
||||
[[nodiscard]] inline hz::ExpectedValue<bool, SmartctlJsonParserError>
|
||||
get_node_exists(const nlohmann::json& root, std::string_view path)
|
||||
{
|
||||
auto node_result = get_node(root, path);
|
||||
if (node_result.has_value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (node_result.error().data()) {
|
||||
case SmartctlJsonParserError::PathNotFound:
|
||||
return false;
|
||||
|
||||
case SmartctlJsonParserError::UnexpectedObjectInPath:
|
||||
case SmartctlJsonParserError::EmptyPath:
|
||||
case SmartctlJsonParserError::InternalError:
|
||||
case SmartctlJsonParserError::TypeError:
|
||||
break;
|
||||
}
|
||||
|
||||
return hz::UnexpectedFrom(node_result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// A signature for a property retrieval function.
|
||||
using PropertyRetrievalFunc = std::function<
|
||||
auto(const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
@@ -137,6 +182,30 @@ inline auto string_formatter()
|
||||
|
||||
|
||||
|
||||
/// Return a lambda which returns a return_property if conditional_path exists.
|
||||
/// If the path doesn't exist, an error is returned.
|
||||
inline auto conditional_formatter(const std::string_view conditional_path, AtaStorageProperty return_property)
|
||||
{
|
||||
return [conditional_path, return_property](const nlohmann::json& root_node, const std::string& key, [[maybe_unused]] const std::string& displayable_name) mutable
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
auto node_exists_result = get_node_exists(root_node, conditional_path);
|
||||
if (!node_exists_result.has_value()) {
|
||||
return hz::Unexpected(SmartctlParserError::DataError, node_exists_result.error().message());
|
||||
}
|
||||
|
||||
if (node_exists_result.value()) {
|
||||
return_property.generic_name = key;
|
||||
return_property.displayable_name = displayable_name;
|
||||
return return_property;
|
||||
}
|
||||
|
||||
return hz::Unexpected(SmartctlParserError::InternalError, std::format("Error getting key {} from JSON data.", key));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Return a lambda which retrieves a key value as a bool (formatted according to parameters), and sets it as a property.
|
||||
inline auto bool_formatter(const std::string_view& true_str, const std::string_view& false_str)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ Copyright:
|
||||
#define SMARTCTL_PARSER_H
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "ata_storage_property.h"
|
||||
|
||||
@@ -9,20 +9,26 @@ Copyright:
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include "smartctl_text_ata_parser.h"
|
||||
|
||||
// #include "local_glibmm.h"
|
||||
#include <clocale> // localeconv
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
|
||||
#include "ata_storage_property.h"
|
||||
#include "hz/string_algo.h" // string_*
|
||||
#include "hz/string_num.h" // string_is_numeric, number_to_string
|
||||
#include "hz/debug.h" // debug_*
|
||||
|
||||
#include "app_pcrecpp.h"
|
||||
#include "smartctl_text_ata_parser.h"
|
||||
#include "ata_storage_property_descr.h"
|
||||
//#include "ata_storage_property_descr.h"
|
||||
// #include "warning_colors.h"
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
#include "smartctl_text_parser_helper.h"
|
||||
|
||||
@@ -1042,7 +1048,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_
|
||||
cap_prop.generic_name = "ata_smart_data/capabilities/_group";
|
||||
|
||||
} else if (re_error_log_cap_group.PartialMatch(cap_prop.reported_name)) {
|
||||
cap_prop.generic_name = "ata_smart_data/capabilities/error_logging_supported";
|
||||
cap_prop.generic_name = "ata_smart_data/capabilities/error_logging_supported/_group";
|
||||
|
||||
} else if (re_sct_cap_group.PartialMatch(cap_prop.reported_name)) {
|
||||
cap_prop.generic_name = "ata_sct_capabilities/_group";
|
||||
@@ -1059,7 +1065,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextAtaParser::parse_section_data_
|
||||
|
||||
AtaStorageProperty p;
|
||||
p.section = AtaStorageProperty::Section::Internal;
|
||||
p.set_name("ata_smart_data/self_test/status/passed");
|
||||
p.set_name("ata_smart_data/self_test/status/_merged");
|
||||
|
||||
AtaStorageSelftestEntry sse;
|
||||
sse.test_num = 0;
|
||||
|
||||
@@ -9,30 +9,34 @@ Copyright:
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include "smartctl_text_basic_parser.h"
|
||||
|
||||
// #include "local_glibmm.h"
|
||||
//#include <clocale> // localeconv
|
||||
//#include <cstdint>
|
||||
//#include <utility>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
|
||||
#include "ata_storage_property.h"
|
||||
#include "hz/string_algo.h" // string_*
|
||||
//#include "hz/string_num.h" // string_is_numeric, number_to_string
|
||||
#include "hz/string_num.h" // string_is_numeric, number_to_string
|
||||
//#include "hz/debug.h" // debug_*
|
||||
|
||||
#include "app_pcrecpp.h"
|
||||
//#include "smartctl_text_ata_parser.h"
|
||||
//#include "ata_storage_property_descr.h"
|
||||
// #include "warning_colors.h"
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
#include "smartctl_text_basic_parser.h"
|
||||
#include "hz/string_num.h"
|
||||
#include "smartctl_text_parser_helper.h"
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string_view smartctl_output)
|
||||
{
|
||||
// perform any2unix
|
||||
std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output));
|
||||
const std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output));
|
||||
|
||||
if (output.empty()) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n");
|
||||
@@ -192,7 +196,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string
|
||||
std::string size;
|
||||
if (app_pcre_match("/^User Capacity:[ \\t]*(.*)$/mi", output, &size)) {
|
||||
int64_t bytes = 0;
|
||||
std::string readable_size = SmartctlTextParserHelper::parse_byte_size(size, bytes, false);
|
||||
const std::string readable_size = SmartctlTextParserHelper::parse_byte_size(size, bytes, false);
|
||||
AtaStorageProperty p;
|
||||
p.set_name("User Capacity", "user_capacity/bytes", "Capacity");
|
||||
p.reported_value = size;
|
||||
|
||||
@@ -6,6 +6,7 @@ Copyright:
|
||||
#ifndef STORAGE_PROPERTY_REPOSITORY_H
|
||||
#define STORAGE_PROPERTY_REPOSITORY_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ata_storage_property.h"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ Copyright:
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
#include <tl/expected.hpp>
|
||||
#include <utility>
|
||||
// #include <stacktrace>
|
||||
|
||||
|
||||
@@ -94,14 +95,25 @@ class ErrorContainer {
|
||||
|
||||
|
||||
|
||||
/// ExpectedValue is a wrapper around tl::expected that uses ErrorContainer as the error type.
|
||||
template<typename ValueType, typename ErrorType>
|
||||
using ExpectedValue = tl::expected<ValueType, ErrorContainer<ErrorType>>;
|
||||
|
||||
|
||||
/// ExpectedVoid is a wrapper around tl::expected that uses ErrorContainer as the error type and void as value.
|
||||
template<typename ErrorType>
|
||||
using ExpectedVoid = tl::expected<void, ErrorContainer<ErrorType>>;
|
||||
|
||||
|
||||
/// Unexpected creates an unexpected value with an ErrorContainer as error.
|
||||
template<typename ErrorContainerWithData>
|
||||
auto UnexpectedFromContainer(const ErrorContainerWithData& container)
|
||||
{
|
||||
return tl::unexpected(container);
|
||||
}
|
||||
|
||||
|
||||
/// Unexpected creates an unexpected value with an ErrorContainer.
|
||||
template<typename ErrorData>
|
||||
auto Unexpected(ErrorData&& data, std::string error_message,
|
||||
const std::source_location& loc = std::source_location::current()
|
||||
@@ -109,13 +121,22 @@ auto Unexpected(ErrorData&& data, std::string error_message,
|
||||
)
|
||||
{
|
||||
return tl::unexpected(ErrorContainer<ErrorData>(std::forward<ErrorData>(data),
|
||||
std::move(error_message),
|
||||
std::move(error_message),
|
||||
loc
|
||||
// trace
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/// UnexpectedFrom creates an unexpected value from ExpectedValue or ExpectedVoid
|
||||
/// containing an error.
|
||||
template<typename ExpectedValueT>
|
||||
auto UnexpectedFrom(ExpectedValueT unexpected_value)
|
||||
{
|
||||
return tl::unexpected(unexpected_value.error());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
std::ostream& operator<< (std::ostream& os, const std::source_location& location)
|
||||
|
||||
Reference in New Issue
Block a user