108 lines
3.2 KiB
Elixir
108 lines
3.2 KiB
Elixir
defmodule PrymnWeb.SystemInfo do
|
|
use PrymnWeb, :live_component
|
|
|
|
require Logger
|
|
alias Phoenix.LiveView.AsyncResult
|
|
|
|
@impl true
|
|
def update(assigns, socket) do
|
|
{:ok,
|
|
socket
|
|
|> assign(:ip, assigns.ip)
|
|
|> assign(:sys_info, AsyncResult.loading())
|
|
|> start_async(:get_sys_info, fn ->
|
|
Logger.debug("getting initial system info for #{assigns.ip}...")
|
|
Prymn.Agents.get_sys_info(assigns.ip)
|
|
end)}
|
|
end
|
|
|
|
@impl true
|
|
def render(assigns) do
|
|
%{result: unwrapped_info} = assigns.sys_info
|
|
assigns = assign(assigns, unwrapped_info: unwrapped_info)
|
|
|
|
~H"""
|
|
<div>
|
|
<div :if={@sys_info.ok?} class="flex justify-between rounded bg-gray-800 p-5 text-white">
|
|
<div>
|
|
<p class="text-xl"><%= @unwrapped_info.uptime || "" %>s</p>
|
|
<p class="text-sm">Uptime</p>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-xl"><%= Enum.count(@unwrapped_info.cpus || []) %></p>
|
|
<p class="text-sm">CPUs</p>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-xl"><%= calculate_cpu_usage(@unwrapped_info.cpus) %></p>
|
|
<p class="text-sm">CPU%</p>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-xl">
|
|
<%= bytes_to_gigabytes(@unwrapped_info.mem_total_bytes - @unwrapped_info.mem_avail_bytes) %>
|
|
<span>/</span>
|
|
<%= bytes_to_gigabytes(@unwrapped_info.mem_total_bytes) %>
|
|
<span>GiB</span>
|
|
</p>
|
|
<p class="text-sm">Memory</p>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-xl">
|
|
<%= calculate_disk_used_percent(@unwrapped_info.disks) %>
|
|
<span>%</span>
|
|
</p>
|
|
<p class="text-sm">Used Disk</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
@impl true
|
|
def handle_async(:get_sys_info, {:ok, {:ok, sys_info}}, socket) do
|
|
%{sys_info: sys_info_result, ip: host_address} = socket.assigns
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(:sys_info, AsyncResult.ok(sys_info_result, sys_info))
|
|
|> start_async(:get_sys_info, fn ->
|
|
Logger.debug("getting more system info for #{host_address}...")
|
|
# 10 seconds is >5 which is gun's timeout duration (which might have a race
|
|
# condition if they are equal)
|
|
Process.sleep(:timer.seconds(10))
|
|
Prymn.Agents.get_sys_info(host_address)
|
|
end)}
|
|
end
|
|
|
|
def handle_async(:get_sys_info, {:ok, {:error, grpc_error}}, socket) do
|
|
%{sys_info: sys_info_result} = socket.assigns
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(:sys_info, AsyncResult.failed(sys_info_result, grpc_error))}
|
|
end
|
|
|
|
def handle_async(:get_sys_info, {:exit, reason}, socket) do
|
|
# dbg(reason)
|
|
{:noreply, socket}
|
|
end
|
|
|
|
defp calculate_cpu_usage(cpus) do
|
|
(Enum.reduce(cpus, 0, fn x, acc -> x.usage + acc end) / Enum.count(cpus))
|
|
|> Float.round(2)
|
|
end
|
|
|
|
defp bytes_to_gigabytes(bytes) do
|
|
Float.round(bytes / Integer.pow(1024, 3), 2)
|
|
end
|
|
|
|
defp calculate_disk_used_percent(disks) do
|
|
alias PrymnProto.Prymn.SysInfoResponse.Disk
|
|
|
|
{used, total} =
|
|
Enum.reduce(disks, {0, 0}, fn %Disk{} = disk, {used, total} ->
|
|
{used + disk.total_bytes - disk.avail_bytes, total + disk.total_bytes}
|
|
end)
|
|
|
|
Float.round(100 * used / total, 2)
|
|
end
|
|
end
|