You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
4.0 KiB
JavaScript
130 lines
4.0 KiB
JavaScript
"use strict";
|
|
|
|
const Promise = require("bluebird");
|
|
const dlayerSource = require("../../packages/dlayer-source");
|
|
const treecutter = require("../../packages/treecutter");
|
|
const upperSnakeCase = require("../../packages/upper-snake-case");
|
|
const { B } = require("../../packages/unit-bytes-iec");
|
|
|
|
const types = require("./");
|
|
|
|
module.exports = function Drive ({ path }) {
|
|
return dlayerSource.withSources({
|
|
path: path,
|
|
blockDevice: async function(_, { $getProperty }) {
|
|
if (await $getProperty(this, "interface") === "nvme") {
|
|
return null;
|
|
} else {
|
|
return types.BlockDevice({ path: path });
|
|
}
|
|
},
|
|
allBlockDevices: async function(_, { $getProperty, sources }) {
|
|
return Promise.try(async () => {
|
|
if (await $getProperty(this, "interface") === "nvme") {
|
|
return Promise.try(() => {
|
|
return sources.nvmeListNamespaces.load(path);
|
|
}).map((namespaceID) => {
|
|
return `${path}n${namespaceID}`;
|
|
});
|
|
} else {
|
|
return [ path ];
|
|
}
|
|
}).then((rootPaths) => {
|
|
let queries = rootPaths.map((path) => ({ path: path }));
|
|
return sources.lsblk.loadMany(queries);
|
|
}).map((blockDeviceTree) => {
|
|
return treecutter.map(blockDeviceTree, (device) => types.BlockDevice(device));
|
|
}).then((resultArray) => {
|
|
// Treecutter always returns an array, regardless of whether the input was an array or not, so we need to flatten it since we will only ever have a single root entry per rootPath query here
|
|
return resultArray.flat();
|
|
});
|
|
},
|
|
size: async function (_, { $getProperty, sources }) {
|
|
if (await $getProperty(this, "interface") === "nvme") {
|
|
return Promise.try(() => {
|
|
return sources.nvmeIdentifyController.load(path);
|
|
}).then((result) => {
|
|
return result.totalSpace;
|
|
});
|
|
} else {
|
|
return Promise.try(() => {
|
|
return sources.lsblk.load({ path: path });
|
|
}).then((result) => {
|
|
return result.size;
|
|
});
|
|
}
|
|
},
|
|
$sources: {
|
|
smartctlScan: {
|
|
[dlayerSource.ID]: path,
|
|
interface: "interface"
|
|
},
|
|
smartctlInfo: {
|
|
[dlayerSource.ID]: path,
|
|
// NOTE: We allow allowable errors here because the SMART subsystem failing doesn't affect any other aspect of the drive's information, so the Drive object as a whole should not yield an error
|
|
[dlayerSource.AllowErrors]: true,
|
|
model: "model",
|
|
modelFamily: "modelFamily",
|
|
smartAvailable: "smartAvailable",
|
|
smartEnabled: "smartEnabled",
|
|
serialNumber: "serialNumber",
|
|
wwn: "wwn",
|
|
firmwareVersion: "firmwareVersion",
|
|
// size: "size",
|
|
rpm: "rpm",
|
|
logicalSectorSize: (device) => device.sectorSizes.logical,
|
|
physicalSectorSize: (device) => device.sectorSizes.physical,
|
|
formFactor: "formFactor",
|
|
ataVersion: "ataVersion",
|
|
sataVersion: "sataVersion"
|
|
},
|
|
smartctlAttributes: {
|
|
[dlayerSource.ID]: path,
|
|
[dlayerSource.AllowErrors]: true,
|
|
smartFunctioning: (attributes) => {
|
|
return (attributes.isOK);
|
|
},
|
|
smartAttributes: (attributesResult) => {
|
|
if (attributesResult.isOK) {
|
|
let attributes = attributesResult.value();
|
|
|
|
return attributes.map((attribute) => {
|
|
return {
|
|
... attribute,
|
|
type: upperSnakeCase(attribute.type),
|
|
updatedWhen: upperSnakeCase(attribute.updatedWhen)
|
|
};
|
|
});
|
|
} else {
|
|
return [];
|
|
}
|
|
},
|
|
smartHealth: (attributesResult) => {
|
|
if (attributesResult.isOK) {
|
|
let attributes = attributesResult.value();
|
|
// FIXME: This is getting values in an inconsistent format? Different for SATA vs. NVMe
|
|
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";
|
|
}
|
|
} else {
|
|
// We can't get SMART data
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|