Show file system inode usage and read-only status
This commit is contained in:
parent
1e87b08b54
commit
23008b4997
23
bin/malpd
23
bin/malpd
@ -3,6 +3,7 @@
|
|||||||
require "socket"
|
require "socket"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "json"
|
require "json"
|
||||||
|
require "shellwords"
|
||||||
|
|
||||||
SOCKET_PATH = "/run/malpd/malpd.sock"
|
SOCKET_PATH = "/run/malpd/malpd.sock"
|
||||||
|
|
||||||
@ -63,8 +64,16 @@ def read_smart(name)
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_inodes(mount)
|
||||||
|
out = `stat -f -c '%c %d' #{Shellwords.escape(mount)} 2>/dev/null`.strip
|
||||||
|
return nil if out.empty?
|
||||||
|
total, free = out.split.map(&:to_i)
|
||||||
|
return nil unless total && total > 0
|
||||||
|
[total, total - free]
|
||||||
|
end
|
||||||
|
|
||||||
def read_filesystems
|
def read_filesystems
|
||||||
out = `findmnt -J -b -o SOURCE,TARGET,FSTYPE,SIZE,USED 2>/dev/null`
|
out = `findmnt -J -b -o SOURCE,TARGET,FSTYPE,SIZE,USED,OPTIONS 2>/dev/null`
|
||||||
return [] if out.empty?
|
return [] if out.empty?
|
||||||
data = JSON.parse(out) rescue nil
|
data = JSON.parse(out) rescue nil
|
||||||
return [] unless data
|
return [] unless data
|
||||||
@ -86,14 +95,22 @@ def read_filesystems
|
|||||||
key = [source, target]
|
key = [source, target]
|
||||||
next if seen[key]
|
next if seen[key]
|
||||||
seen[key] = true
|
seen[key] = true
|
||||||
result << {
|
|
||||||
|
opts = node["options"].to_s.split(",")
|
||||||
|
entry = {
|
||||||
"type" => "filesystem",
|
"type" => "filesystem",
|
||||||
"source" => source,
|
"source" => source,
|
||||||
"mount" => target,
|
"mount" => target,
|
||||||
"fstype" => fstype,
|
"fstype" => fstype,
|
||||||
"size" => size,
|
"size" => size,
|
||||||
"used" => node["used"].to_i
|
"used" => node["used"].to_i,
|
||||||
|
"readonly" => opts.include?("ro")
|
||||||
}
|
}
|
||||||
|
if (ino = read_inodes(target))
|
||||||
|
entry["inode_total"] = ino[0]
|
||||||
|
entry["inode_used"] = ino[1]
|
||||||
|
end
|
||||||
|
result << entry
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|||||||
@ -210,21 +210,42 @@ if cgi.params.key?("content")
|
|||||||
used_b = entry["used"].to_i
|
used_b = entry["used"].to_i
|
||||||
pct_f = size_b > 0 ? (used_b * 100.0 / size_b) : 0.0
|
pct_f = size_b > 0 ? (used_b * 100.0 / size_b) : 0.0
|
||||||
pct = pct_f.round
|
pct = pct_f.round
|
||||||
cls = pct_f >= 90 ? "bad" : pct_f >= 75 ? "warn" : "ok"
|
space_cls = pct_f >= 90 ? "bad" : pct_f >= 75 ? "warn" : "ok"
|
||||||
used_h = CGI.escapeHTML(human_capacity(used_b))
|
used_h = CGI.escapeHTML(human_capacity(used_b))
|
||||||
size_h = CGI.escapeHTML(human_capacity(size_b))
|
size_h = CGI.escapeHTML(human_capacity(size_b))
|
||||||
|
|
||||||
|
fs_stats = []
|
||||||
|
inode_cls = "ok"
|
||||||
|
if (itot = entry["inode_total"].to_i) > 0
|
||||||
|
iused = entry["inode_used"].to_i
|
||||||
|
ipct_f = iused * 100.0 / itot
|
||||||
|
inode_cls = ipct_f >= 90 ? "bad" : ipct_f >= 75 ? "warn" : "ok"
|
||||||
|
fs_stats << [inode_cls, "Inodes #{ipct_f.round}%"]
|
||||||
|
end
|
||||||
|
fs_stats << ["warn", "READ-ONLY"] if entry["readonly"]
|
||||||
|
|
||||||
|
severities = [space_cls, inode_cls]
|
||||||
|
card_cls = severities.include?("bad") ? "bad" :
|
||||||
|
severities.include?("warn") ? "warn" : "ok"
|
||||||
|
|
||||||
|
stats_html = fs_stats.map { |c, txt|
|
||||||
|
%(<span class="stat #{c}">#{CGI.escapeHTML(txt)}</span>)
|
||||||
|
}.join
|
||||||
|
stats_block = fs_stats.empty? ? "" :
|
||||||
|
%(<div class="drive-stats" style="margin-top:0.6rem;">#{stats_html}</div>)
|
||||||
|
|
||||||
html << <<~HTML
|
html << <<~HTML
|
||||||
<div class="card #{cls}">
|
<div class="card #{card_cls}">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="card-title">#{mount} <span class="card-capacity">#{fstype}</span></span>
|
<span class="card-title">#{mount} <span class="card-capacity">#{fstype}</span></span>
|
||||||
<span class="badge #{cls}">#{pct}%</span>
|
<span class="badge #{space_cls}">#{pct}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:0.5rem;margin-bottom:0.3rem;">
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:0.5rem;margin-bottom:0.3rem;">
|
||||||
<span style="font-size:0.72rem;color:#64748b;font-weight:600;">#{source}</span>
|
<span style="font-size:0.72rem;color:#64748b;font-weight:600;">#{source}</span>
|
||||||
<span class="subitem-value #{cls}">#{used_h} / #{size_h}</span>
|
<span class="subitem-value #{space_cls}">#{used_h} / #{size_h}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="bar-track" style="height:6px;"><div class="bar-fill #{cls}" style="width:#{pct}%"></div></div>
|
<div class="bar-track" style="height:6px;"><div class="bar-fill #{space_cls}" style="width:#{pct}%"></div></div>
|
||||||
|
#{stats_block}
|
||||||
</div>
|
</div>
|
||||||
HTML
|
HTML
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user