WIP: Move SMART health logic to GraphQL API, and further complete SMART support in API

feature/node-rewrite
Sven Slootweg 6 years ago
parent e9619f387a
commit 85c7c9a803

@ -44,6 +44,7 @@
"pug": "^2.0.0-beta6",
"rfr": "^1.2.3",
"scrypt-for-humans": "^2.0.5",
"snake-case": "^2.1.0",
"split": "^1.0.0",
"sse-channel": "^3.1.1",
"through2": "^2.0.1",

@ -12,6 +12,7 @@ const matchOrError = require("./match-or-error");
const lsblk = require("./wrappers/lsblk");
const smartctl = require("./wrappers/smartctl");
const lvm = require("./wrappers/lvm");
const upperSnakeCase = require("./upper-snake-case");
function gql(strings) {
return strings.join("");
@ -312,6 +313,34 @@ function createDrive({ path }) {
formFactor: "formFactor",
ataVersion: "ataVersion",
sataVersion: "sataVersion"
},
smartctlAttributes: {
[ID]: path,
smartAttributes: (attributes) => {
return attributes.map((attribute) => {
return Object.assign({}, attribute, {
type: upperSnakeCase(attribute.type),
updatedWhen: upperSnakeCase(attribute.updatedWhen)
});
});
},
smartHealth: (attributes) => {
let failed = attributes.filter((item) => {
return (item.failingNow === true || item.failedBefore === true);
});
let deteriorating = attributes.filter((item) => {
return (item.type === "preFail" && item.worstValueSeen < 100);
});
if (failed.length > 0) {
return "FAILING";
} else if (deteriorating.length > 0) {
return "DETERIORATING";
} else {
return "HEALTHY";
}
}
}
});
}
@ -402,39 +431,52 @@ return Promise.try(() => {
let query = gql`
# query SomeDrives($drivePaths: [String]) {
query SomeDrives {
# hardware {
# drives(paths: $drivePaths) {
# path
# interface
# model
# modelFamily
# smartAvailable
# smartEnabled
# serialNumber
# wwn
# firmwareVersion
# size
# rpm
# logicalSectorSize
# physicalSectorSize
# formFactor
# ataVersion
# sataVersion
# blockDevice {
# removable
# children {
# name
# mountpoint
# size
# }
# }
# }
# }
hardware {
drives {
path
interface
model
modelFamily
smartAvailable
smartEnabled
serialNumber
wwn
firmwareVersion
size
rpm
logicalSectorSize
physicalSectorSize
formFactor
ataVersion
sataVersion
smartHealth
# smartAttributes {
# name
# type
# value
# failingNow
# flags {
# affectsPerformance
# indicatesFailure
# }
# }
# blockDevice {
# removable
# children {
# name
# mountpoint
# size
# }
# }
}
}
resources {
# resources {
# blockDevices {
# name
# mountpoint
@ -455,30 +497,30 @@ return Promise.try(() => {
# }
# }
lvm {
physicalVolumes {
path
blockDevice {
name
deviceNumber
}
volumeGroup {
name
}
format
size
freeSpace
duplicate
allocatable
used
exported
missing
}
}
}
# lvm {
# physicalVolumes {
# path
# blockDevice {
# name
# deviceNumber
# }
# volumeGroup {
# name
# }
# format
# size
# freeSpace
# duplicate
# allocatable
# used
# exported
# missing
# }
# }
# }
}
`;

@ -7,29 +7,11 @@ const smartctl = require("../wrappers/smartctl");
const lvm = require("../wrappers/lvm");
const {B} = require("../units/bytes/iec");
/* FIXME: Move this to GraphQL API */
function getSmartStatus(smartData) {
let failed = smartData.filter((item) => {
return (item.failingNow === true || item.failedBefore === true);
});
let deteriorating = smartData.filter((item) => {
return (item.type === "preFail" && item.worstValueSeen < 100);
});
if (failed.length > 0) {
return "failed";
} else if (deteriorating.length > 0) {
return "deteriorating";
} else {
return "healthy";
}
}
function getStorageDevices() {
return Promise.try(() => {
return lsblk();
}).filter((device) => {
/* FIXME: Move device type filter to GraphQL? */
return (device.type === "disk");
}).map((device) => {
return Object.assign({}, device, {

@ -252,10 +252,34 @@ type SmartAttributeFlags {
indicatesFailure: Boolean!
}
enum SmartAttributeType {
PRE_FAIL
OLD_AGE
}
enum SmartAttributeUpdateType {
ALWAYS
OFFLINE
}
enum SmartHealth {
HEALTHY
DETERIORATING
FAILING
}
type SmartAttribute {
id: Int!
name: String!
flags: SmartAttributeFlags
flags: SmartAttributeFlags!
value: Int!
rawValue: String!
worstValueSeen: Int!
failureThreshold: Int!
type: SmartAttributeType!
failingNow: Boolean!
failedBefore: Boolean!
updatedWhen: SmartAttributeUpdateType!
}
type BlockDevice {
@ -276,6 +300,8 @@ type PhysicalDrive {
blockDevice: BlockDevice!
smartAvailable: Boolean!
smartEnabled: Boolean
smartHealth: SmartHealth
smartAttributes: [SmartAttribute!]!
model: String
modelFamily: String
serialNumber: String
@ -288,7 +314,6 @@ type PhysicalDrive {
formFactor: String
ataVersion: String
sataVersion: String
smartAttributes: [SmartAttribute!]!
}
type LVMPhysicalVolume {

@ -0,0 +1,7 @@
"use strict";
const snakeCase = require("snake-case");
module.exports = function upperSnakeCase(value) {
return snakeCase(value).toUpperCase();
};

@ -53,6 +53,7 @@ module.exports = {
"Old_age": "oldAge"
}),
failingNow: (failedWhen === "FAILING_NOW"),
/* TODO: Should the below include the FAILING_NOW state? */
failedBefore: (failedWhen === "In_the_past"),
updatedWhen: mapValue(updatedWhen, {
"Always": "always",

@ -6193,6 +6193,11 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
lower-case@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
lowercase-keys@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@ -6624,6 +6629,13 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
no-case@^2.2.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
dependencies:
lower-case "^1.1.1"
node-gyp@^3.3.1:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
@ -8671,6 +8683,13 @@ slice-ansi@^2.1.0:
astral-regex "^1.0.0"
is-fullwidth-code-point "^2.0.0"
snake-case@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=
dependencies:
no-case "^2.2.0"
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"

Loading…
Cancel
Save