mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-19 07:48:08 +00:00
Merge pull request #1451 from balena-io/device-metadata
Add device system information to state endpoint patch
This commit is contained in:
34
package-lock.json
generated
34
package-lock.json
generated
@ -613,6 +613,12 @@
|
|||||||
"integrity": "sha1-qIc1gLOoS2msHmhzI7Ffu+uQR5o=",
|
"integrity": "sha1-qIc1gLOoS2msHmhzI7Ffu+uQR5o=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/os-utils": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/os-utils/-/os-utils-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-r5a1I4xr8e25GFiLdpHV+jHUawtbmtuCQHR1P3SH/Y1gmRy0KLjBVGe5uUkODU/6GntoGPhOu05jv+sBdAVpog==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
@ -1311,7 +1317,7 @@
|
|||||||
},
|
},
|
||||||
"util": {
|
"util": {
|
||||||
"version": "0.10.3",
|
"version": "0.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
"resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
||||||
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
|
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -3370,7 +3376,7 @@
|
|||||||
},
|
},
|
||||||
"stream-combiner": {
|
"stream-combiner": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
|
"resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
|
||||||
"integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
|
"integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -3716,7 +3722,7 @@
|
|||||||
},
|
},
|
||||||
"event-stream": {
|
"event-stream": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
|
"resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
|
||||||
"integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
|
"integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -6204,7 +6210,7 @@
|
|||||||
},
|
},
|
||||||
"map-stream": {
|
"map-stream": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
|
||||||
"integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=",
|
"integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@ -6393,7 +6399,7 @@
|
|||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@ -6544,7 +6550,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.15.1",
|
"version": "2.15.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
"resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
|
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@ -7151,6 +7157,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||||
},
|
},
|
||||||
|
"os-utils": {
|
||||||
|
"version": "0.0.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-utils/-/os-utils-0.0.14.tgz",
|
||||||
|
"integrity": "sha1-KeURaXsZgrjGJ3Ihdf45eX72QVY="
|
||||||
|
},
|
||||||
"osenv": {
|
"osenv": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||||
@ -7705,7 +7716,7 @@
|
|||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
@ -8584,7 +8595,7 @@
|
|||||||
},
|
},
|
||||||
"split": {
|
"split": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
|
"resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz",
|
||||||
"integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
|
"integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -8692,7 +8703,7 @@
|
|||||||
},
|
},
|
||||||
"stream-combiner": {
|
"stream-combiner": {
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
|
"resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
|
||||||
"integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
|
"integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -8842,6 +8853,11 @@
|
|||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systeminformation": {
|
||||||
|
"version": "4.27.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.27.3.tgz",
|
||||||
|
"integrity": "sha512-0Nc8AYEK818h7FI+bbe/kj7xXsMD5zOHvO9alUqQH/G4MHXu5tHQfWqC/bzWOk4JtoQPhnyLgxMYncDA2eeSBw=="
|
||||||
|
},
|
||||||
"tapable": {
|
"tapable": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
|
||||||
|
@ -29,8 +29,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dbus": "^1.0.7",
|
"dbus": "^1.0.7",
|
||||||
"mdns-resolver": "^1.0.0",
|
"mdns-resolver": "^1.0.0",
|
||||||
|
"os-utils": "0.0.14",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"sqlite3": "^4.1.1"
|
"sqlite3": "^4.1.1",
|
||||||
|
"systeminformation": "^4.27.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.16.2",
|
"node": "^12.16.2",
|
||||||
@ -56,6 +58,7 @@
|
|||||||
"@types/morgan": "^1.9.0",
|
"@types/morgan": "^1.9.0",
|
||||||
"@types/mz": "0.0.32",
|
"@types/mz": "0.0.32",
|
||||||
"@types/node": "^12.12.54",
|
"@types/node": "^12.12.54",
|
||||||
|
"@types/os-utils": "0.0.1",
|
||||||
"@types/request": "^2.48.5",
|
"@types/request": "^2.48.5",
|
||||||
"@types/rimraf": "^2.0.4",
|
"@types/rimraf": "^2.0.4",
|
||||||
"@types/rwlock": "^5.0.2",
|
"@types/rwlock": "^5.0.2",
|
||||||
|
@ -11,6 +11,8 @@ import DeviceState from '../device-state';
|
|||||||
import { CoreOptions } from 'request';
|
import { CoreOptions } from 'request';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
|
||||||
|
import * as sysInfo from '../lib/system-info';
|
||||||
|
|
||||||
// The exponential backoff starts at 15s
|
// The exponential backoff starts at 15s
|
||||||
const MINIMUM_BACKOFF_DELAY = 15000;
|
const MINIMUM_BACKOFF_DELAY = 15000;
|
||||||
|
|
||||||
@ -121,6 +123,39 @@ const getStateDiff = (): DeviceStatus => {
|
|||||||
.value(),
|
.value(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shouldReportSysInfo = (type: string, past: number, now: number) => {
|
||||||
|
if (!past) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: Deduplicate this code
|
||||||
|
switch (type) {
|
||||||
|
case 'cpu_usage':
|
||||||
|
// The bucket size of cpu usage is 20
|
||||||
|
return Math.floor(past / 20) !== Math.floor(now / 20);
|
||||||
|
case 'cpu_temp':
|
||||||
|
return Math.floor(past / 5) !== Math.floor(now / 5);
|
||||||
|
case 'memory_usage':
|
||||||
|
return Math.floor(past / 10) !== Math.floor(now / 10);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toOmit: string[] = [];
|
||||||
|
_.each(diff.local, (value, key) => {
|
||||||
|
// if we have some system information that has changed, we check that it's
|
||||||
|
// within a certain range before reporting
|
||||||
|
if (
|
||||||
|
!shouldReportSysInfo(
|
||||||
|
key,
|
||||||
|
(lastReportedLocal as any)[key],
|
||||||
|
value as number,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
toOmit.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
diff.local = _.omit(diff.local, toOmit);
|
||||||
return _.omitBy(diff, _.isEmpty);
|
return _.omitBy(diff, _.isEmpty);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -171,8 +206,16 @@ const reportCurrentState = (): null => {
|
|||||||
reportPending = true;
|
reportPending = true;
|
||||||
try {
|
try {
|
||||||
const currentDeviceState = await deviceState.getStatus();
|
const currentDeviceState = await deviceState.getStatus();
|
||||||
_.assign(stateForReport.local, currentDeviceState.local);
|
const info = await sysInfo.getSysInfoToReport();
|
||||||
_.assign(stateForReport.dependent, currentDeviceState.dependent);
|
stateForReport.local = {
|
||||||
|
...stateForReport.local,
|
||||||
|
...currentDeviceState.local,
|
||||||
|
...info,
|
||||||
|
};
|
||||||
|
stateForReport.dependent = {
|
||||||
|
...stateForReport.dependent,
|
||||||
|
...currentDeviceState.dependent,
|
||||||
|
};
|
||||||
|
|
||||||
const stateDiff = getStateDiff();
|
const stateDiff = getStateDiff();
|
||||||
if (_.size(stateDiff) === 0) {
|
if (_.size(stateDiff) === 0) {
|
||||||
|
113
src/lib/system-info.ts
Normal file
113
src/lib/system-info.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import * as systeminformation from 'systeminformation';
|
||||||
|
import * as osUtils from 'os-utils';
|
||||||
|
import { fs, child_process } from 'mz';
|
||||||
|
|
||||||
|
export function getCpuUsage() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
osUtils.cpuUsage((percent) => {
|
||||||
|
resolve(Math.round(percent * 100));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockDeviceRegex = /(\/dev\/.*)p\d+/;
|
||||||
|
export async function getStorageInfo() {
|
||||||
|
const fsInfo = await systeminformation.fsSize();
|
||||||
|
let mainFs: string | undefined;
|
||||||
|
let total = 0;
|
||||||
|
// First we find the block device which the data partition is part of
|
||||||
|
for (const partition of fsInfo) {
|
||||||
|
if (partition.mount === '/data') {
|
||||||
|
const match = partition.fs.match(blockDeviceRegex);
|
||||||
|
if (match == null) {
|
||||||
|
mainFs = undefined;
|
||||||
|
} else {
|
||||||
|
mainFs = match[1];
|
||||||
|
total = partition.size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainFs) {
|
||||||
|
return {
|
||||||
|
blockDevice: '',
|
||||||
|
storageUsed: undefined,
|
||||||
|
storageTotal: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let used = 0;
|
||||||
|
for (const partition of fsInfo) {
|
||||||
|
if (partition.fs.startsWith(mainFs)) {
|
||||||
|
used += partition.used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
blockDevice: mainFs,
|
||||||
|
storageUsed: bytesToMb(used),
|
||||||
|
storageTotal: bytesToMb(total),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMemoryInformation() {
|
||||||
|
const mem = await systeminformation.mem();
|
||||||
|
return {
|
||||||
|
used: bytesToMb(mem.used),
|
||||||
|
total: bytesToMb(mem.total),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCpuTemp() {
|
||||||
|
return Math.round((await systeminformation.cpuTemperature()).main);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCpuId() {
|
||||||
|
// Read /proc/device-tree/serial-number
|
||||||
|
// if it's not there, return undefined
|
||||||
|
try {
|
||||||
|
const buffer = await fs.readFile('/proc/device-tree/serial-number');
|
||||||
|
// Remove the null byte at the end
|
||||||
|
return buffer.toString('utf-8').substr(0, buffer.length - 2);
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const undervoltageRegex = /under.*voltage/;
|
||||||
|
export async function undervoltageDetected() {
|
||||||
|
try {
|
||||||
|
const [dmesgStdout] = await child_process.exec('dmesg');
|
||||||
|
return undervoltageRegex.test(dmesgStdout.toString());
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSysInfoToReport() {
|
||||||
|
const [cpu, mem, temp, cpuid, storage, undervoltage] = await Promise.all([
|
||||||
|
getCpuUsage(),
|
||||||
|
getMemoryInformation(),
|
||||||
|
getCpuTemp(),
|
||||||
|
getCpuId(),
|
||||||
|
getStorageInfo(),
|
||||||
|
undervoltageDetected(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
cpu_usage: cpu,
|
||||||
|
memory_usage: mem.used,
|
||||||
|
memory_total: mem.total,
|
||||||
|
storage_usage: storage.storageUsed,
|
||||||
|
storage_total: storage.storageTotal,
|
||||||
|
storage_block_device: storage.blockDevice,
|
||||||
|
cpu_temp: temp,
|
||||||
|
cpu_id: cpuid,
|
||||||
|
is_undervolted: undervoltage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function bytesToMb(bytes: number) {
|
||||||
|
return Math.floor(bytes / 1024 / 1024);
|
||||||
|
}
|
Reference in New Issue
Block a user