"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 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 ( ) ;
} ) ;
} ,
$sources : {
lsblk : {
[ dlayerSource . ID ] : { path } ,
size : "size"
} ,
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 : ( attributes ) => {
if ( attributes . isOK ) {
return attributes . map ( ( attribute ) => {
return {
... attribute ,
type : upperSnakeCase ( attribute . type ) ,
updatedWhen : upperSnakeCase ( attribute . updatedWhen )
} ;
} ) ;
} else {
return [ ] ;
}
} ,
smartHealth : ( attributes ) => {
if ( attributes . isOK ) {
// FIXME: This is getting values in an inconsistent format? Different for SATA vs. NVMe
console . log ( "foo" , { 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" ;
}
} else {
// If we can't get SMART data, the only safe assumption is that it must be failing
return "FAILING" ;
}
}
}
}
} ) ;
} ;