Add file systems
This commit is contained in:
parent
a282debfbd
commit
1e87b08b54
@ -367,6 +367,12 @@
|
|||||||
margin: 1.5rem 0 0.5rem;
|
margin: 1.5rem 0 0.5rem;
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
37
bin/malpd
37
bin/malpd
@ -63,6 +63,41 @@ def read_smart(name)
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_filesystems
|
||||||
|
out = `findmnt -J -b -o SOURCE,TARGET,FSTYPE,SIZE,USED 2>/dev/null`
|
||||||
|
return [] if out.empty?
|
||||||
|
data = JSON.parse(out) rescue nil
|
||||||
|
return [] unless data
|
||||||
|
|
||||||
|
skip_fstypes = %w[tmpfs devtmpfs efivarfs]
|
||||||
|
result = []
|
||||||
|
seen = {}
|
||||||
|
stack = Array(data["filesystems"]).dup
|
||||||
|
until stack.empty?
|
||||||
|
node = stack.shift
|
||||||
|
stack.concat(node["children"]) if node["children"]
|
||||||
|
source = node["source"].to_s
|
||||||
|
fstype = node["fstype"].to_s
|
||||||
|
target = node["target"].to_s
|
||||||
|
next unless source.start_with?("/dev/")
|
||||||
|
next if skip_fstypes.include?(fstype)
|
||||||
|
size = node["size"].to_i
|
||||||
|
next if size <= 0
|
||||||
|
key = [source, target]
|
||||||
|
next if seen[key]
|
||||||
|
seen[key] = true
|
||||||
|
result << {
|
||||||
|
"type" => "filesystem",
|
||||||
|
"source" => source,
|
||||||
|
"mount" => target,
|
||||||
|
"fstype" => fstype,
|
||||||
|
"size" => size,
|
||||||
|
"used" => node["used"].to_i
|
||||||
|
}
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
def handle_info(conn)
|
def handle_info(conn)
|
||||||
info = []
|
info = []
|
||||||
|
|
||||||
@ -89,6 +124,8 @@ def handle_info(conn)
|
|||||||
info << entry
|
info << entry
|
||||||
end
|
end
|
||||||
|
|
||||||
|
info.concat(read_filesystems)
|
||||||
|
|
||||||
if conn
|
if conn
|
||||||
conn.puts JSON.generate(info)
|
conn.puts JSON.generate(info)
|
||||||
else
|
else
|
||||||
|
|||||||
@ -7,6 +7,18 @@ require "json"
|
|||||||
require "securerandom"
|
require "securerandom"
|
||||||
require "socket"
|
require "socket"
|
||||||
|
|
||||||
|
def human_capacity(bytes)
|
||||||
|
units = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||||
|
i = 0
|
||||||
|
size = bytes.to_f
|
||||||
|
while size >= 1000 && i < units.length - 1
|
||||||
|
size /= 1000
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
precision = i <= 3 ? 0 : 1
|
||||||
|
"%g %s" % [size.round(precision), units[i]]
|
||||||
|
end
|
||||||
|
|
||||||
ASSETS_DIR = File.join(__dir__, "../assets")
|
ASSETS_DIR = File.join(__dir__, "../assets")
|
||||||
DATA_DIR = File.join(__dir__, "../data")
|
DATA_DIR = File.join(__dir__, "../data")
|
||||||
SESSIONS_FILE = File.join(DATA_DIR, "sessions.txt")
|
SESSIONS_FILE = File.join(DATA_DIR, "sessions.txt")
|
||||||
@ -145,9 +157,14 @@ if cgi.params.key?("content")
|
|||||||
stats
|
stats
|
||||||
end
|
end
|
||||||
|
|
||||||
info.each do |entry|
|
drives = info.select { |e| e["type"] == "drive" }
|
||||||
case entry["type"]
|
filesystems = info.select { |e| e["type"] == "filesystem" }
|
||||||
when "drive"
|
|
||||||
|
if drives.any?
|
||||||
|
html << %(<div class="span-2">)
|
||||||
|
html << %(<div class="section-label">Storage Drives</div>)
|
||||||
|
html << %(<div class="stack">)
|
||||||
|
drives.each do |entry|
|
||||||
dt = entry["drivetype"]
|
dt = entry["drivetype"]
|
||||||
name = CGI.escapeHTML(entry["name"].to_s)
|
name = CGI.escapeHTML(entry["name"].to_s)
|
||||||
model = CGI.escapeHTML(entry["model"])
|
model = CGI.escapeHTML(entry["model"])
|
||||||
@ -169,7 +186,7 @@ if cgi.params.key?("content")
|
|||||||
title = name.empty? ? model : "#{name} · #{model}"
|
title = name.empty? ? model : "#{name} · #{model}"
|
||||||
|
|
||||||
html << <<~HTML
|
html << <<~HTML
|
||||||
<div class="card #{card_cls} span-2">
|
<div class="card #{card_cls}">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="card-title">#{title} <span class="card-capacity">#{capacity}</span> <span class="drive-type #{dt}">#{dt.upcase}</span></span>
|
<span class="card-title">#{title} <span class="card-capacity">#{capacity}</span> <span class="drive-type #{dt}">#{dt.upcase}</span></span>
|
||||||
</div>
|
</div>
|
||||||
@ -177,6 +194,42 @@ if cgi.params.key?("content")
|
|||||||
</div>
|
</div>
|
||||||
HTML
|
HTML
|
||||||
end
|
end
|
||||||
|
html << %(</div>)
|
||||||
|
html << %(</div>)
|
||||||
|
end
|
||||||
|
|
||||||
|
if filesystems.any?
|
||||||
|
html << %(<div>)
|
||||||
|
html << %(<div class="section-label">File Systems</div>)
|
||||||
|
html << %(<div class="stack">)
|
||||||
|
filesystems.each do |entry|
|
||||||
|
mount = CGI.escapeHTML(entry["mount"].to_s)
|
||||||
|
source = CGI.escapeHTML(entry["source"].to_s)
|
||||||
|
fstype = CGI.escapeHTML(entry["fstype"].to_s)
|
||||||
|
size_b = entry["size"].to_i
|
||||||
|
used_b = entry["used"].to_i
|
||||||
|
pct_f = size_b > 0 ? (used_b * 100.0 / size_b) : 0.0
|
||||||
|
pct = pct_f.round
|
||||||
|
cls = pct_f >= 90 ? "bad" : pct_f >= 75 ? "warn" : "ok"
|
||||||
|
used_h = CGI.escapeHTML(human_capacity(used_b))
|
||||||
|
size_h = CGI.escapeHTML(human_capacity(size_b))
|
||||||
|
|
||||||
|
html << <<~HTML
|
||||||
|
<div class="card #{cls}">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">#{mount} <span class="card-capacity">#{fstype}</span></span>
|
||||||
|
<span class="badge #{cls}">#{pct}%</span>
|
||||||
|
</div>
|
||||||
|
<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 class="subitem-value #{cls}">#{used_h} / #{size_h}</span>
|
||||||
|
</div>
|
||||||
|
<div class="bar-track" style="height:6px;"><div class="bar-fill #{cls}" style="width:#{pct}%"></div></div>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
html << %(</div>)
|
||||||
|
html << %(</div>)
|
||||||
end
|
end
|
||||||
|
|
||||||
cgi.out("type" => "text/html", "charset" => "UTF-8") do
|
cgi.out("type" => "text/html", "charset" => "UTF-8") do
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user