Skip to content

Commit

Permalink
metrics: collect disk usage from internal metrics
Browse files Browse the repository at this point in the history
use `mount.total` and `mount.used` from internal metrics
instead of parsing `df` output.
  • Loading branch information
tomasmatus authored and jelly committed Oct 8, 2024
1 parent 2ba803a commit 8b687a1
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 73 deletions.
85 changes: 16 additions & 69 deletions pkg/metrics/metrics.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ const CURRENT_METRICS = [
{ name: "cpu.core.nice", derive: "rate" },
{ name: "disk.dev.read", units: "bytes", derive: "rate" },
{ name: "disk.dev.written", units: "bytes", derive: "rate" },
{ name: "mount.total", units: "bytes" },
{ name: "mount.used", units: "bytes" },
];

const CPU_TEMPERATURE_METRICS = [
Expand Down Expand Up @@ -294,15 +296,11 @@ class CurrentMetrics extends React.Component {
this.onMetricsUpdate = this.onMetricsUpdate.bind(this);
this.onTemperatureUpdate = this.onTemperatureUpdate.bind(this);
this.onPrivilegedMetricsUpdate = this.onPrivilegedMetricsUpdate.bind(this);
this.updateMounts = this.updateMounts.bind(this);
this.updateLoad = this.updateLoad.bind(this);

cockpit.addEventListener("visibilitychange", this.onVisibilityChange);
this.onVisibilityChange();

// regularly update info about filesystems
this.updateMounts();

// there is no internal metrics channel for load yet; see https://github.com/cockpit-project/cockpit/pull/14510
this.updateLoad();
}
Expand Down Expand Up @@ -351,71 +349,6 @@ class CurrentMetrics extends React.Component {
}
}

/* Return Set of mount points which should not be shown in Disks card */
hideMounts(procMounts) {
const result = new Set();
procMounts.trim().split("\n")
.forEach(line => {
// looks like this: /dev/loop1 /var/mnt iso9660 ro,relatime,nojoliet,check=s,map=n,blocksize=2048 0 0
const fields = line.split(' ');
const options = fields[3].split(',');

/* hide read-only loop mounts; these are often things like snaps or iso images
* which are always at 100% capacity, but are uninteresting for disk usage alerts */
if ((fields[0].indexOf("/loop") >= 0 && options.indexOf('ro') >= 0))
result.add(fields[1]);
/* hide flatpaks */
if ((fields[0].indexOf('revokefs-fuse') >= 0 && fields[1].indexOf('flatpak') >= 0))
result.add(fields[1]);
});
return result;
}

updateMounts() {
Promise.all([
/* df often exits with non-zero if it encounters any filesystem it can't read;
but that's fine, get info about all the others */
cockpit.script("df --local --exclude-type=tmpfs --exclude-type=devtmpfs --block-size=1 --output=target,size,avail,pcent || true",
{ err: "message" }),
cockpit.file("/proc/mounts").read()
])
.then(([df_out, mounts_out]) => {
const hide = this.hideMounts(mounts_out);

// skip first line with the headings
const mounts = [];
df_out.trim()
.split("\n")
.slice(1)
.forEach(s => {
const fields = s.split(/ +/);
if (fields.length != 4) {
console.warn("Invalid line in df:", s);
return;
}

if (hide.has(fields[0]))
return;
mounts.push({
target: fields[0],
size: Number(fields[1]),
avail: Number(fields[2]),
use: Number(fields[3].slice(0, -1)), /* strip off '%' */
});
});

debug("df parsing done:", JSON.stringify(mounts));
this.setState({ mounts });

// update it again regularly
window.setTimeout(this.updateMounts, 10000);
})
.catch(ex => {
console.warn("Failed to run df or read /proc/mounts:", ex.toString());
this.setState({ mounts: [] });
});
}

updateLoad() {
cockpit.file("/proc/loadavg").read()
.then(content => {
Expand Down Expand Up @@ -477,6 +410,8 @@ class CurrentMetrics extends React.Component {
this.cgroupMemoryNames = data.metrics[10].instances.slice();
console.assert(data.metrics[14].name === 'disk.dev.read');
this.disksNames = data.metrics[14].instances.slice();
console.assert(data.metrics[16].name === 'mount.total');
this.mountPoints = data.metrics[16].instances.slice();
debug("metrics message was meta, new net instance names", JSON.stringify(this.netInterfacesNames));
return;
}
Expand Down Expand Up @@ -560,6 +495,18 @@ class CurrentMetrics extends React.Component {
if (notMappedContainers.length !== 0) {
this.update_podman_name_mapping(notMappedContainers);
}

const mountsTotal = this.samples[16];
const mountsUsed = this.samples[17];
newState.mounts = mountsTotal.map((mountTotal, i) => {
return {
target: this.mountPoints[i],
size: mountTotal,
avail: mountTotal - mountsUsed[i],
use: Math.round(mountsUsed[i] / mountTotal * 100),
};
});

this.setState(newState);
}

Expand Down
16 changes: 12 additions & 4 deletions src/cockpit/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,15 +398,23 @@ def sample(self, samples: Samples) -> None:
if line[0] != '/':
continue

path = line.split()[1]
fs_spec, fs_file, _fs_vfstype, fs_mntopts, *_rest = line.split()

# Skip read-only loop mounts
if '/loop' in fs_spec and 'ro' in fs_mntopts.split(','):
continue
# Hide flatpaks
if 'revokefs-fuse' in fs_spec and 'flatpak' in fs_file:
continue

try:
res = os.statvfs(path)
res = os.statvfs(fs_file)
except OSError:
continue
frsize = res.f_frsize
total = frsize * res.f_blocks
samples['mount.total'][path] = total
samples['mount.used'][path] = total - frsize * res.f_bfree
samples['mount.total'][fs_file] = total
samples['mount.used'][fs_file] = total - frsize * res.f_bfree


class BlockSampler(Sampler):
Expand Down

0 comments on commit 8b687a1

Please sign in to comment.