Merge pull request 'UI: display block devices' (#1635) from hsjobeki/clan-core:hsjobeki-main into main

This commit is contained in:
clan-bot
2024-06-19 09:39:41 +00:00
4 changed files with 117 additions and 9 deletions

View File

@@ -1,9 +1,11 @@
import json
import os import os
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Literal from typing import Any, Literal
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.nix import nix_shell, run_no_stdout
from . import API from . import API
@@ -81,3 +83,46 @@ def get_directory(current_path: str) -> Directory:
) )
return directory return directory
@dataclass
class BlkInfo:
name: str
rm: str
size: str
ro: bool
mountpoints: list[str]
type_: Literal["disk"]
@dataclass
class Blockdevices:
blockdevices: list[BlkInfo]
def blk_from_dict(data: dict) -> BlkInfo:
return BlkInfo(
name=data["name"],
rm=data["rm"],
size=data["size"],
ro=data["ro"],
mountpoints=data["mountpoints"],
type_=data["type"], # renamed here
)
@API.register
def show_block_devices() -> Blockdevices:
"""
Abstract api method to show block devices.
It must return a list of block devices.
"""
cmd = nix_shell(["nixpkgs#util-linux"], ["lsblk", "--json"])
proc = run_no_stdout(cmd)
res = proc.stdout.strip()
blk_info: dict[str, Any] = json.loads(res)
return Blockdevices(
blockdevices=[blk_from_dict(device) for device in blk_info["blockdevices"]]
)

View File

@@ -3,6 +3,7 @@ import { MachineListView } from "./routes/machines/view";
import { colors } from "./routes/colors/view"; import { colors } from "./routes/colors/view";
import { clan } from "./routes/clan/view"; import { clan } from "./routes/clan/view";
import { HostList } from "./routes/hosts/view"; import { HostList } from "./routes/hosts/view";
import { BlockDevicesView } from "./routes/blockdevices/view";
export type Route = keyof typeof routes; export type Route = keyof typeof routes;
@@ -22,6 +23,11 @@ export const routes = {
label: "hosts", label: "hosts",
icon: "devices_other", icon: "devices_other",
}, },
blockdevices: {
child: BlockDevicesView,
label: "blockdevices",
icon: "devices_other",
},
colors: { colors: {
child: colors, child: colors,
label: "Colors", label: "Colors",

View File

@@ -0,0 +1,61 @@
import { route } from "@/src/App";
import { OperationResponse, pyApi } from "@/src/api";
import { Component, For, Show, createEffect, createSignal } from "solid-js";
type DevicesModel = Extract<
OperationResponse<"show_block_devices">,
{ status: "success" }
>["data"]["blockdevices"];
export const BlockDevicesView: Component = () => {
const [devices, setServices] = createSignal<DevicesModel>();
pyApi.show_block_devices.receive((r) => {
const { status } = r;
if (status === "error") return console.error(r.errors);
setServices(r.data.blockdevices);
});
createEffect(() => {
if (route() === "blockdevices") pyApi.show_block_devices.dispatch({});
});
return (
<div>
<div class="tooltip" data-tip="Refresh">
<button
class="btn btn-ghost"
onClick={() => pyApi.show_block_devices.dispatch({})}
>
<span class="material-icons ">refresh</span>
</button>
</div>
<div class="flex max-w-screen-lg flex-col gap-4">
<Show when={devices()}>
{(devices) => (
<For each={devices()}>
{(device) => (
<div class="stats shadow">
<div class="stat w-28 py-8">
<div class="stat-title">Name</div>
<div class="stat-value">
{" "}
<span class="material-icons">storage</span> {device.name}
</div>
<div class="stat-desc"></div>
</div>
<div class="stat w-28">
<div class="stat-title">Size</div>
<div class="stat-value">{device.size}</div>
<div class="stat-desc"></div>
</div>
</div>
)}
</For>
)}
</Show>
</div>
</div>
);
};

View File

@@ -1,16 +1,12 @@
import { import {
For, For,
Match,
Show, Show,
Switch,
createEffect, createEffect,
createSignal, createSignal,
type Component, type Component,
} from "solid-js"; } from "solid-js";
import { useMachineContext } from "../../Config";
import { route } from "@/src/App"; import { route } from "@/src/App";
import { OperationResponse, pyApi } from "@/src/api"; import { OperationResponse, pyApi } from "@/src/api";
import toast from "solid-toast";
type ServiceModel = Extract< type ServiceModel = Extract<
OperationResponse<"show_mdns">, OperationResponse<"show_mdns">,
@@ -40,13 +36,13 @@ export const HostList: Component = () => {
<span class="material-icons ">refresh</span> <span class="material-icons ">refresh</span>
</button> </button>
</div> </div>
<div class="flex gap-2 flex-wrap"> <div class="flex flex-wrap gap-2">
<Show when={services()}> <Show when={services()}>
{(services) => ( {(services) => (
<For each={Object.values(services())}> <For each={Object.values(services())}>
{(service) => ( {(service) => (
<div class="rounded-lg bg-white p-5 shadow-lg w-[30rem]"> <div class="w-[30rem] rounded-lg bg-white p-5 shadow-lg">
<div class="stats shadow flex flex-col"> <div class="stats flex flex-col shadow">
<div class="stat"> <div class="stat">
<div class="stat-title">Host</div> <div class="stat-title">Host</div>
<div class="stat-value">{service.host}</div> <div class="stat-value">{service.host}</div>
@@ -61,7 +57,7 @@ export const HostList: Component = () => {
</div> </div>
<div class="join join-vertical w-full px-0"> <div class="join join-vertical w-full px-0">
<div class="collapse collapse-arrow join-item"> <div class="collapse join-item collapse-arrow">
<input type="radio" name="my-accordion-4" /> <input type="radio" name="my-accordion-4" />
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
Details Details