defmodule PrymnWeb.ServerLive.Show do use PrymnWeb, :live_view require Logger alias Prymn.{Agents, Servers} @impl true def mount(_params, _session, socket) do {:ok, socket} end @impl true def handle_params(%{"id" => id}, _, socket) do server = Servers.get_server!(id) if connected?(socket) and server.status == :registered do Agents.subscribe_to_health(server.public_ip) Agents.start_connection(server.public_ip) send(self(), :get_sys_info) end health = Agents.get_health(server.public_ip) {:noreply, socket |> assign(:health, health || %{message: "Connecting...", tasks: []}) |> assign(:page_title, server.name) |> assign(:server, server) |> assign(:dry_run, false) |> assign(:update_output, []) |> assign(:sys_info, nil) # TODO: Do not assign this to the socket - instead generate it in the HTML |> assign(:registration_command, Servers.create_setup_command(server))} end @impl true def handle_info(:get_sys_info, socket) do status = get_in(socket.assigns, [:health, Access.key(:status)]) host_address = get_in(socket.assigns, [:server, Access.key(:public_ip)]) pid = self() if host_address != nil and status == :connected do Task.start_link(fn -> {:ok, sys_info} = Agents.get_sys_info(host_address) send(pid, sys_info) end) end # 10 seconds is >5 which is gun's timeout duration (which might have a race # condition if they are equal) Process.send_after(self(), :get_sys_info, :timer.seconds(10)) {:noreply, socket} end def handle_info(%PrymnProto.Prymn.SysInfoResponse{} = response, socket) do # TODO: GRPC calls should be done through wrapper functions. Necessary # calculations should be done then and there. {:noreply, socket |> assign(:sys_info, response) |> assign(:updates_available, response.updates_available) |> assign(:cpus, response.cpus)} end def handle_info(%PrymnProto.Prymn.SysUpdateResponse{} = response, socket) do output = String.split(response.output, "\n") socket = assign(socket, :update_output, output) {:noreply, socket} end def handle_info(%Agents.Health{} = health, socket) do {:noreply, assign(socket, :health, health)} end @impl true def handle_event("system_update", _params, socket) do host_address = get_in(socket.assigns, [:server, Access.key(:public_ip)]) server_name = get_in(socket.assigns, [:server, Access.key(:name)]) socket = if host_address do Agents.sys_update(host_address, socket.assigns.dry_run) put_flash(socket, :info, "Started a system update on server #{server_name}.") else put_flash( socket, :error, "Could not perform the update. Your server does not seem to have an address" ) end {:noreply, socket} end def handle_event("change_dry_run", %{"dry_run" => enabled}, socket) do enabled = (enabled == "true" && true) || false {:noreply, assign(socket, :dry_run, enabled)} 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