WIP
parent
98a4bcb6d8
commit
f85ab90be1
@ -1,249 +0,0 @@
|
||||
{
|
||||
const matchValue = require("match-value");
|
||||
const syncpipe = require("syncpipe");
|
||||
|
||||
const {B} = require("../unit-bytes-iec");
|
||||
const mapAttributeFlags = require("./map-attribute-flags");
|
||||
}
|
||||
|
||||
RootInfo
|
||||
= header:Header infoSection:InfoSection Newline* {
|
||||
return { ...header, fields: infoSection }
|
||||
};
|
||||
|
||||
RootScan
|
||||
= devices:ScanDevice* {
|
||||
return { devices: devices };
|
||||
}
|
||||
|
||||
RootAttributes
|
||||
= header:Header attributesSection:AttributesSection Newline* {
|
||||
return { ...header, attributes: attributesSection }
|
||||
};
|
||||
|
||||
_
|
||||
= (" " / "\t")*
|
||||
|
||||
RestOfLine
|
||||
= content:$[^\n]+ Newline {
|
||||
return content;
|
||||
}
|
||||
|
||||
Newline
|
||||
= "\n"
|
||||
/ "\r\n"
|
||||
|
||||
Header 'header'
|
||||
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
|
||||
return { versionString, copyrightStatement };
|
||||
}
|
||||
|
||||
BytesValue
|
||||
= value:SeparatedNumberValue {
|
||||
return B(value);
|
||||
}
|
||||
|
||||
NumberValue
|
||||
= value:$[0-9]+ {
|
||||
return parseInt(value);
|
||||
}
|
||||
|
||||
SeparatedNumberValue
|
||||
= value:$[0-9,]+ {
|
||||
return syncpipe(value, [
|
||||
(_) => _.replace(/,/g, ""),
|
||||
(_) => parseInt(_)
|
||||
]);
|
||||
}
|
||||
|
||||
HexNumberValue
|
||||
= value:$[0-9A-Fa-f]+ {
|
||||
return parseInt(value, 16);
|
||||
}
|
||||
|
||||
IdentifierValue
|
||||
= value:$[a-zA-Z_-]+ {
|
||||
return value;
|
||||
}
|
||||
|
||||
// smartctl --scan
|
||||
|
||||
ScanDevice 'scanned device'
|
||||
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
|
||||
return { path: path, interface: interface_ };
|
||||
}
|
||||
|
||||
// smartctl --info
|
||||
|
||||
InfoSection 'information section'
|
||||
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
|
||||
return fields.filter((field) => field != null);
|
||||
}
|
||||
|
||||
InfoField 'information field'
|
||||
= InfoFieldSimple
|
||||
/ InfoFieldIgnored
|
||||
/ InfoFieldSize
|
||||
/ InfoFieldRPM
|
||||
/ InfoFieldSectorSizes
|
||||
/ InfoFieldBoolean
|
||||
/ InfoFieldUnknown
|
||||
|
||||
InfoFieldSimpleKey
|
||||
= "Device Model" { return "model"; }
|
||||
/ "Model Number" { return "model"; }
|
||||
/ "Model Family" { return "modelFamily"; }
|
||||
/ "Serial Number" { return "serialNumber"; }
|
||||
/ "LU WWN Device Id" { return "wwn"; }
|
||||
/ "Firmware Version" { return "firmwareVersion"; }
|
||||
/ "Form Factor" { return "formFactor"; }
|
||||
/ "ATA Version is" { return "ataVersion"; }
|
||||
/ "SATA Version is" { return "sataVersion"; }
|
||||
|
||||
InfoFieldSimple
|
||||
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
|
||||
return { key: key, value: value };
|
||||
}
|
||||
|
||||
InfoFieldUnknown
|
||||
= key:$[^:]+ ":" _ RestOfLine {
|
||||
console.warn(`Encountered unrecognized SMART info key: ${key}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
InfoFieldIgnoredKey
|
||||
= "Device is"
|
||||
/ "Local Time is"
|
||||
|
||||
InfoFieldIgnored
|
||||
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
|
||||
return null;
|
||||
}
|
||||
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
|
||||
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
|
||||
return null;
|
||||
}
|
||||
|
||||
InfoFieldSize
|
||||
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
|
||||
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
|
||||
return {
|
||||
key: "size",
|
||||
value: B(value)
|
||||
};
|
||||
}
|
||||
|
||||
InfoFieldSizeKey
|
||||
= "User Capacity:"
|
||||
/ "Total NVM Capacity:"
|
||||
|
||||
InfoFieldRPM
|
||||
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
|
||||
return {
|
||||
key: "rpm",
|
||||
value: value
|
||||
};
|
||||
}
|
||||
|
||||
InfoFieldSectorSizes
|
||||
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
|
||||
return {
|
||||
key: "sectorSizes",
|
||||
value: {
|
||||
logical: logicalSize,
|
||||
physical: physicalSize
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
InfoFieldBooleanKey
|
||||
= "SMART support is" { return "smartEnabled"; }
|
||||
|
||||
InfoFieldBoolean
|
||||
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
|
||||
return {
|
||||
key: key,
|
||||
value: matchValue(value, {
|
||||
Enabled: true,
|
||||
Disabled: false
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// smartctl --attributes
|
||||
|
||||
AttributesSection
|
||||
= AttributesSectionSATA
|
||||
/ AttributesSectionNVMe
|
||||
|
||||
AttributesSectionSATA
|
||||
= "=== START OF READ SMART DATA SECTION ===" Newline
|
||||
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
|
||||
"Vendor Specific SMART Attributes with Thresholds:" Newline
|
||||
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
|
||||
attributes:AttributeFieldSATA+ {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
AttributesSectionNVMe
|
||||
= "=== START OF SMART DATA SECTION ===" Newline
|
||||
"SMART/Health Information (NVMe Log 0x02)" Newline
|
||||
attributes:AttributeFieldNVMe+ {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
AttributeFlags
|
||||
= "0x" number:HexNumberValue {
|
||||
return mapAttributeFlags(number);
|
||||
}
|
||||
|
||||
AttributeUpdatedWhen
|
||||
= "Always"
|
||||
/ "Offline"
|
||||
|
||||
AttributeFailedWhen
|
||||
= "FAILING_NOW"
|
||||
/ "In_the_past"
|
||||
/ "-"
|
||||
|
||||
AttributeFieldType
|
||||
= "Pre-fail"
|
||||
/ "Old_age"
|
||||
|
||||
AttributeFieldSATA
|
||||
= _ id:NumberValue
|
||||
_ attributeName:IdentifierValue
|
||||
_ flags:AttributeFlags
|
||||
_ value:NumberValue
|
||||
_ worstValue:NumberValue
|
||||
_ threshold:NumberValue
|
||||
_ type:AttributeFieldType
|
||||
_ updatedWhen:AttributeUpdatedWhen
|
||||
_ failedWhen:AttributeFailedWhen
|
||||
_ rawValue:RestOfLine {
|
||||
return {
|
||||
id,
|
||||
attributeName,
|
||||
flags,
|
||||
value,
|
||||
worstValue,
|
||||
threshold,
|
||||
rawValue,
|
||||
updatedWhen: matchValue(updatedWhen, {
|
||||
"Always": "always",
|
||||
"Offline": "offline"
|
||||
}),
|
||||
type: matchValue(type, {
|
||||
"Pre-fail": "preFail",
|
||||
"Old_age": "oldAge"
|
||||
}),
|
||||
failingNow: (failedWhen === "FAILING_NOW"),
|
||||
/* TODO: Should the below include the FAILING_NOW state? */
|
||||
failedBefore: (failedWhen === "In_the_past")
|
||||
};
|
||||
}
|
||||
|
||||
AttributeFieldNVMe
|
||||
= label:$[^:]+ ":" _ value:RestOfLine {
|
||||
return { label: label, value };
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
import { _, RestOfLine, Newline, NumberValue, HexNumberValue, IdentifierValue } from "../primitives"
|
||||
import { Header } from "../shared"
|
||||
|
||||
{
|
||||
const matchValue = require("match-value");
|
||||
const mapAttributeFlags = require("../../map-attribute-flags");
|
||||
}
|
||||
|
||||
RootAttributes
|
||||
= header:Header attributesSection:AttributesSection Newline* {
|
||||
return { ...header, attributes: attributesSection }
|
||||
};
|
||||
|
||||
AttributesSection
|
||||
= AttributesSectionSATA
|
||||
/ AttributesSectionNVMe
|
||||
|
||||
AttributesSectionSATA
|
||||
= "=== START OF READ SMART DATA SECTION ===" Newline
|
||||
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
|
||||
"Vendor Specific SMART Attributes with Thresholds:" Newline
|
||||
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
|
||||
attributes:AttributeFieldSATA+ {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
AttributesSectionNVMe
|
||||
= "=== START OF SMART DATA SECTION ===" Newline
|
||||
"SMART/Health Information (NVMe Log 0x02)" Newline
|
||||
attributes:AttributeFieldNVMe+ {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
AttributeFlags
|
||||
= "0x" number:HexNumberValue {
|
||||
return mapAttributeFlags(number);
|
||||
}
|
||||
|
||||
AttributeUpdatedWhen
|
||||
= "Always"
|
||||
/ "Offline"
|
||||
|
||||
AttributeFailedWhen
|
||||
= "FAILING_NOW"
|
||||
/ "In_the_past"
|
||||
/ "-"
|
||||
|
||||
AttributeFieldType
|
||||
= "Pre-fail"
|
||||
/ "Old_age"
|
||||
|
||||
AttributeFieldSATA
|
||||
= _ id:NumberValue
|
||||
_ attributeName:IdentifierValue
|
||||
_ flags:AttributeFlags
|
||||
_ value:NumberValue
|
||||
_ worstValue:NumberValue
|
||||
_ threshold:NumberValue
|
||||
_ type:AttributeFieldType
|
||||
_ updatedWhen:AttributeUpdatedWhen
|
||||
_ failedWhen:AttributeFailedWhen
|
||||
_ rawValue:RestOfLine {
|
||||
return {
|
||||
id,
|
||||
attributeName,
|
||||
flags,
|
||||
value,
|
||||
worstValue,
|
||||
threshold,
|
||||
rawValue,
|
||||
updatedWhen: matchValue(updatedWhen, {
|
||||
"Always": "always",
|
||||
"Offline": "offline"
|
||||
}),
|
||||
type: matchValue(type, {
|
||||
"Pre-fail": "preFail",
|
||||
"Old_age": "oldAge"
|
||||
}),
|
||||
failingNow: (failedWhen === "FAILING_NOW"),
|
||||
/* TODO: Should the below include the FAILING_NOW state? */
|
||||
failedBefore: (failedWhen === "In_the_past")
|
||||
};
|
||||
}
|
||||
|
||||
AttributeFieldNVMe
|
||||
= label:$[^:]+ ":" _ value:RestOfLine {
|
||||
return { label: label, value };
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
import { _, RestOfLine, Newline, NumberValue, SeparatedNumberValue, BytesValue } from "../primitives"
|
||||
import { Header } from "../shared"
|
||||
|
||||
{
|
||||
const matchValue = require("match-value");
|
||||
}
|
||||
|
||||
RootInfo
|
||||
= header:Header infoSection:InfoSection Newline* {
|
||||
return { ...header, fields: infoSection }
|
||||
};
|
||||
|
||||
InfoSection 'information section'
|
||||
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
|
||||
return fields.filter((field) => field != null);
|
||||
}
|
||||
|
||||
InfoField 'information field'
|
||||
= InfoFieldSimple
|
||||
/ InfoFieldIgnored
|
||||
/ InfoFieldSize
|
||||
/ InfoFieldRPM
|
||||
/ InfoFieldSectorSizes
|
||||
/ InfoFieldBoolean
|
||||
/ InfoFieldUnknown
|
||||
|
||||
InfoFieldSimpleKey
|
||||
= "Device Model" { return "model"; }
|
||||
/ "Model Number" { return "model"; }
|
||||
/ "Model Family" { return "modelFamily"; }
|
||||
/ "Serial Number" { return "serialNumber"; }
|
||||
/ "LU WWN Device Id" { return "wwn"; }
|
||||
/ "Firmware Version" { return "firmwareVersion"; }
|
||||
/ "Form Factor" { return "formFactor"; }
|
||||
/ "ATA Version is" { return "ataVersion"; }
|
||||
/ "SATA Version is" { return "sataVersion"; }
|
||||
|
||||
InfoFieldSimple
|
||||
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
|
||||
return { key: key, value: value };
|
||||
}
|
||||
|
||||
InfoFieldUnknown
|
||||
= key:$[^:]+ ":" _ RestOfLine {
|
||||
console.warn(`Encountered unrecognized SMART info key: ${key}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
InfoFieldIgnoredKey
|
||||
= "Device is"
|
||||
/ "Local Time is"
|
||||
|
||||
InfoFieldIgnored
|
||||
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
|
||||
return null;
|
||||
}
|
||||
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
|
||||
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
|
||||
return null;
|
||||
}
|
||||
|
||||
InfoFieldSize
|
||||
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
|
||||
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
|
||||
return {
|
||||
key: "size",
|
||||
value: B(value)
|
||||
};
|
||||
}
|
||||
|
||||
InfoFieldSizeKey
|
||||
= "User Capacity:"
|
||||
/ "Total NVM Capacity:"
|
||||
|
||||
InfoFieldRPM
|
||||
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
|
||||
return {
|
||||
key: "rpm",
|
||||
value: value
|
||||
};
|
||||
}
|
||||
|
||||
InfoFieldSectorSizes
|
||||
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
|
||||
return {
|
||||
key: "sectorSizes",
|
||||
value: {
|
||||
logical: logicalSize,
|
||||
physical: physicalSize
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
InfoFieldBooleanKey
|
||||
= "SMART support is" { return "smartEnabled"; }
|
||||
|
||||
InfoFieldBoolean
|
||||
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
|
||||
return {
|
||||
key: key,
|
||||
value: matchValue(value, {
|
||||
Enabled: true,
|
||||
Disabled: false
|
||||
})
|
||||
};
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { _, RestOfLine } from "../primitives"
|
||||
|
||||
RootScan
|
||||
= devices:ScanDevice* {
|
||||
return { devices: devices };
|
||||
}
|
||||
|
||||
ScanDevice 'scanned device'
|
||||
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
|
||||
return { path: path, interface: interface_ };
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
{
|
||||
const syncpipe = require("syncpipe");
|
||||
const {B} = require("../../unit-bytes-iec");
|
||||
}
|
||||
|
||||
_
|
||||
= (" " / "\t")*
|
||||
|
||||
RestOfLine
|
||||
= content:$[^\n]+ Newline {
|
||||
return content;
|
||||
}
|
||||
|
||||
Newline
|
||||
= "\n"
|
||||
/ "\r\n"
|
||||
|
||||
BytesValue
|
||||
= value:SeparatedNumberValue {
|
||||
return B(value);
|
||||
}
|
||||
|
||||
NumberValue
|
||||
= value:$[0-9]+ {
|
||||
return parseInt(value);
|
||||
}
|
||||
|
||||
SeparatedNumberValue
|
||||
= value:$[0-9,]+ {
|
||||
return syncpipe(value, [
|
||||
(_) => _.replace(/,/g, ""),
|
||||
(_) => parseInt(_)
|
||||
]);
|
||||
}
|
||||
|
||||
HexNumberValue
|
||||
= value:$[0-9A-Fa-f]+ {
|
||||
return parseInt(value, 16);
|
||||
}
|
||||
|
||||
IdentifierValue
|
||||
= value:$[a-zA-Z_-]+ {
|
||||
return value;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { RestOfLine, Newline } from "./primitives"
|
||||
|
||||
Header 'header'
|
||||
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
|
||||
return { versionString, copyrightStatement };
|
||||
}
|
Loading…
Reference in New Issue