Create machine: init view with api

This commit is contained in:
Johannes Kirschbauer 2024-07-23 13:56:00 +02:00
parent f787acdaa4
commit d38983c784
Signed by: hsjobeki
SSH Key Fingerprint: SHA256:vX3utDqig7Ph5L0JPv87ZTPb/w7cMzREKVZzzLFg9qU
7 changed files with 163 additions and 18 deletions

View File

@ -1,13 +1,17 @@
import argparse
import logging
import re
from pathlib import Path
from ..api import API
from ..clan_uri import FlakeId
from ..errors import ClanError
from ..git import commit_file
from ..inventory import Machine, MachineDeploy, get_path, load_inventory, save_inventory
from ..inventory import (
Machine,
MachineDeploy,
load_inventory_eval,
load_inventory_json,
save_inventory,
)
log = logging.getLogger(__name__)
@ -20,12 +24,15 @@ def create_machine(flake: FlakeId, machine: Machine) -> None:
"Machine name must be a valid hostname", location="Create Machine"
)
inventory = load_inventory(flake.path)
inventory = load_inventory_json(flake.path)
full_inventory = load_inventory_eval(flake.path)
if machine.name in full_inventory.machines.keys():
raise ClanError(f"Machine with the name {machine.name} already exists")
inventory.machines.update({machine.name: machine})
save_inventory(inventory, flake.path, f"Create machine {machine.name}")
commit_file(get_path(flake.path), Path(flake.path))
def create_command(args: argparse.Namespace) -> None:
create_machine(

View File

@ -1 +1,3 @@
api
api
.vite

View File

@ -8,6 +8,7 @@ import { Flash } from "./routes/flash/view";
import { Settings } from "./routes/settings";
import { Welcome } from "./routes/welcome";
import { Deploy } from "./routes/deploy";
import { CreateMachine } from "./routes/machines/create";
export type Route = keyof typeof routes;
@ -22,6 +23,11 @@ export const routes = {
label: "Machines",
icon: "devices_other",
},
"machines/add": {
child: CreateMachine,
label: "create Machine",
icon: "add",
},
hosts: {
child: HostList,
label: "hosts",

View File

@ -67,6 +67,17 @@ function createFunctions<K extends OperationNames>(
dispatch: (args: OperationArgs<K>) => void;
receive: (fn: (response: OperationResponse<K>) => void, id: string) => void;
} {
window.clan[operationName] = (s: string) => {
const f = (response: OperationResponse<K>) => {
// Get the correct receiver function for the op_key
const receiver = registry[operationName][response.op_key];
if (receiver) {
receiver(response);
}
};
deserialize(f)(s);
};
return {
dispatch: (args: OperationArgs<K>) => {
// Send the data to the gtk app
@ -78,15 +89,6 @@ function createFunctions<K extends OperationNames>(
receive: (fn: (response: OperationResponse<K>) => void, id: string) => {
// @ts-expect-error: This should work although typescript doesn't let us write
registry[operationName][id] = fn;
window.clan[operationName] = (s: string) => {
const f = (response: OperationResponse<K>) => {
if (response.op_key === id) {
registry[operationName][id](response);
}
};
deserialize(f)(s);
};
},
};
}

View File

@ -4,7 +4,6 @@
@tailwind components;
@tailwind utilities;
html {
overflow-x: hidden;
overflow-y: scroll;

View File

@ -0,0 +1,124 @@
import { callApi, OperationArgs, pyApi } from "@/src/api";
import { activeURI } from "@/src/App";
import { createForm, required } from "@modular-forms/solid";
import toast from "solid-toast";
type CreateMachineForm = OperationArgs<"create_machine">;
export function CreateMachine() {
const [formStore, { Form, Field }] = createForm<CreateMachineForm>({});
const handleSubmit = async (values: CreateMachineForm) => {
const active_dir = activeURI();
if (!active_dir) {
toast.error("Open a clan to create the machine in");
return;
}
callApi("create_machine", {
flake: {
loc: active_dir,
},
machine: {
name: "jon",
deploy: {
targetHost: null,
},
},
});
console.log("submit", values);
};
return (
<div class="px-1">
Create new Machine
<Form onSubmit={handleSubmit}>
<Field
name="machine.name"
validate={[required("This field is required")]}
>
{(field, props) => (
<>
<label class="input input-bordered flex items-center gap-2">
<input
type="text"
class="grow"
placeholder="name"
required
{...props}
/>
</label>
<div class="label">
{field.error && (
<span class="label-text-alt font-bold text-error">
{field.error}
</span>
)}
</div>
</>
)}
</Field>
<Field name="machine.description">
{(field, props) => (
<>
<label class="input input-bordered flex items-center gap-2">
<input
type="text"
class="grow"
placeholder="description"
required
{...props}
/>
</label>
<div class="label">
{field.error && (
<span class="label-text-alt font-bold text-error">
{field.error}
</span>
)}
</div>
</>
)}
</Field>
<Field name="machine.deploy.targetHost">
{(field, props) => (
<>
<label class="input input-bordered flex items-center gap-2">
<input
type="text"
class="grow"
placeholder="root@flash-installer.local"
required
{...props}
/>
</label>
<div class="label">
<span class="label-text-alt text-neutral">
Must be set before deployment for the following tasks:
<ul>
<li>
<span>Detect hardware config</span>
</li>
<li>
<span>Detect disk layout</span>
</li>
<li>
<span>Remote installation</span>
</li>
</ul>
</span>
{field.error && (
<span class="label-text-alt font-bold text-error">
{field.error}
</span>
)}
</div>
</>
)}
</Field>
<button class="btn btn-error float-right" type="submit">
<span class="material-icons">add</span>Create
</button>
</Form>
</div>
);
}

View File

@ -7,7 +7,7 @@ import {
createSignal,
type Component,
} from "solid-js";
import { activeURI, route, setActiveURI } from "@/src/App";
import { activeURI, route, setActiveURI, setRoute } from "@/src/App";
import { OperationResponse, callApi, pyApi } from "@/src/api";
import toast from "solid-toast";
import { MachineListItem } from "@/src/components/MachineListItem";
@ -86,6 +86,11 @@ export const MachineListView: Component = () => {
<span class="material-icons ">refresh</span>
</button>
</div>
<div class="tooltip tooltip-bottom" data-tip="Create machine">
<button class="btn btn-ghost" onClick={() => setRoute("machines/add")}>
<span class="material-icons ">add</span>
</button>
</div>
{/* <Show when={services()}>
{(services) => (
<For each={Object.values(services())}>