clan-cli: init vm command
This commit is contained in:
@@ -1,4 +1,11 @@
|
|||||||
{ lib, config, options, ... }:
|
{ lib, config, pkgs, options, extendModules, modulesPath, ... }:
|
||||||
|
let
|
||||||
|
vmConfig = extendModules {
|
||||||
|
modules = [
|
||||||
|
(modulesPath + "/virtualisation/qemu-vm.nix")
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
clan.virtualisation = {
|
clan.virtualisation = {
|
||||||
@@ -33,9 +40,19 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
system.clan.vm.config = {
|
system.clan.vm = {
|
||||||
inherit (config.clan.virtualisation) cores graphics;
|
# for clan vm inspect
|
||||||
memory_size = config.clan.virtualisation.memorySize;
|
config = {
|
||||||
|
inherit (config.clan.virtualisation) cores graphics;
|
||||||
|
memory_size = config.clan.virtualisation.memorySize;
|
||||||
|
};
|
||||||
|
# for clan vm create
|
||||||
|
create = pkgs.writeText "vm.json" (builtins.toJSON {
|
||||||
|
initrd = "${vmConfig.config.system.build.initialRamdisk}/${vmConfig.config.system.boot.loader.initrdFile}";
|
||||||
|
toplevel = vmConfig.config.system.build.toplevel;
|
||||||
|
regInfo = (pkgs.closureInfo { rootPaths = vmConfig.config.virtualisation.additionalPaths; });
|
||||||
|
inherit (config.clan.virtualisation) memorySize cores graphics;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
virtualisation = lib.optionalAttrs (options.virtualisation ? cores) {
|
virtualisation = lib.optionalAttrs (options.virtualisation ? cores) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import sys
|
|||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from . import config, create, machines, secrets, webui
|
from . import config, create, machines, secrets, vms, webui
|
||||||
from .errors import ClanError
|
from .errors import ClanError
|
||||||
from .ssh import cli as ssh_cli
|
from .ssh import cli as ssh_cli
|
||||||
|
|
||||||
@@ -47,6 +47,9 @@ def create_parser(prog: Optional[str] = None) -> argparse.ArgumentParser:
|
|||||||
parser_webui = subparsers.add_parser("webui", help="start webui")
|
parser_webui = subparsers.add_parser("webui", help="start webui")
|
||||||
webui.register_parser(parser_webui)
|
webui.register_parser(parser_webui)
|
||||||
|
|
||||||
|
parser_vms = subparsers.add_parser("vms", help="manage virtual machines")
|
||||||
|
vms.register_parser(parser_vms)
|
||||||
|
|
||||||
if argcomplete:
|
if argcomplete:
|
||||||
argcomplete.autocomplete(parser)
|
argcomplete.autocomplete(parser)
|
||||||
|
|
||||||
|
|||||||
21
pkgs/clan-cli/clan_cli/vms/__init__.py
Normal file
21
pkgs/clan-cli/clan_cli/vms/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
from .create import register_create_parser
|
||||||
|
from .inspect import register_inspect_parser
|
||||||
|
|
||||||
|
|
||||||
|
def register_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
|
subparser = parser.add_subparsers(
|
||||||
|
title="command",
|
||||||
|
description="command to execute",
|
||||||
|
help="the command to execute",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
inspect_parser = subparser.add_parser(
|
||||||
|
"inspect", help="inspect the vm configuration"
|
||||||
|
)
|
||||||
|
register_inspect_parser(inspect_parser)
|
||||||
|
|
||||||
|
create_parser = subparser.add_parser("create", help="create a VM from a machine")
|
||||||
|
register_create_parser(create_parser)
|
||||||
101
pkgs/clan-cli/clan_cli/vms/create.py
Normal file
101
pkgs/clan-cli/clan_cli/vms/create.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ..dirs import get_clan_flake_toplevel
|
||||||
|
from ..nix import nix_build, nix_shell
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_create_info(machine: str) -> dict:
|
||||||
|
clan_dir = get_clan_flake_toplevel().as_posix()
|
||||||
|
|
||||||
|
# config = nix_config()
|
||||||
|
# system = config["system"]
|
||||||
|
|
||||||
|
vm_json = subprocess.run(
|
||||||
|
nix_build(
|
||||||
|
[
|
||||||
|
# f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.clan.virtualisation.createJSON' # TODO use this
|
||||||
|
f'{clan_dir}#nixosConfigurations."{machine}".config.system.clan.vm.create'
|
||||||
|
]
|
||||||
|
),
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
).stdout.strip()
|
||||||
|
with open(vm_json) as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def create(args: argparse.Namespace) -> None:
|
||||||
|
print(f"Creating VM for {args.machine}")
|
||||||
|
machine = args.machine
|
||||||
|
vm_config = get_vm_create_info(machine)
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir_:
|
||||||
|
xchg_dir = Path(tmpdir_) / "xchg"
|
||||||
|
xchg_dir.mkdir()
|
||||||
|
disk_img = f"{tmpdir_}/disk.img"
|
||||||
|
subprocess.run(
|
||||||
|
nix_shell(
|
||||||
|
["qemu"],
|
||||||
|
[
|
||||||
|
"qemu-img",
|
||||||
|
"create",
|
||||||
|
"-f",
|
||||||
|
"raw",
|
||||||
|
disk_img,
|
||||||
|
"1024M",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"mkfs.ext4",
|
||||||
|
"-L",
|
||||||
|
"nixos",
|
||||||
|
disk_img,
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
nix_shell(
|
||||||
|
["qemu"],
|
||||||
|
[
|
||||||
|
# fmt: off
|
||||||
|
"qemu-kvm",
|
||||||
|
"-name", machine,
|
||||||
|
"-m", f'{vm_config["memorySize"]}M',
|
||||||
|
"-smp", str(vm_config["cores"]),
|
||||||
|
"-device", "virtio-rng-pci",
|
||||||
|
"-net", "nic,netdev=user.0,model=virtio", "-netdev", "user,id=user.0",
|
||||||
|
"-virtfs", "local,path=/nix/store,security_model=none,mount_tag=nix-store",
|
||||||
|
"-virtfs", f"local,path={xchg_dir},security_model=none,mount_tag=shared",
|
||||||
|
"-virtfs", f"local,path={xchg_dir},security_model=none,mount_tag=xchg",
|
||||||
|
"-drive", f'cache=writeback,file={disk_img},format=raw,id=drive1,if=none,index=1,werror=report',
|
||||||
|
"-device", "virtio-blk-pci,bootindex=1,drive=drive1,serial=root",
|
||||||
|
"-device", "virtio-keyboard",
|
||||||
|
"-usb",
|
||||||
|
"-device", "usb-tablet,bus=usb-bus.0",
|
||||||
|
"-kernel", f'{vm_config["toplevel"]}/kernel',
|
||||||
|
"-initrd", vm_config["initrd"],
|
||||||
|
"-append", f'{(Path(vm_config["toplevel"]) / "kernel-params").read_text()} init={vm_config["toplevel"]}/init regInfo={vm_config["regInfo"]}/registration console=ttyS0,115200n8 console=tty0',
|
||||||
|
# fmt: on
|
||||||
|
],
|
||||||
|
),
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_create_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
|
parser.add_argument("machine", type=str)
|
||||||
|
parser.set_defaults(func=create)
|
||||||
38
pkgs/clan-cli/clan_cli/vms/inspect.py
Normal file
38
pkgs/clan-cli/clan_cli/vms/inspect.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from ..dirs import get_clan_flake_toplevel
|
||||||
|
from ..nix import nix_eval
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_inspect_info(machine: str) -> dict:
|
||||||
|
clan_dir = get_clan_flake_toplevel().as_posix()
|
||||||
|
|
||||||
|
# config = nix_config()
|
||||||
|
# system = config["system"]
|
||||||
|
|
||||||
|
return json.loads(
|
||||||
|
subprocess.run(
|
||||||
|
nix_eval(
|
||||||
|
[
|
||||||
|
# f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.clan.virtualisation' # TODO use this
|
||||||
|
f'{clan_dir}#nixosConfigurations."{machine}".config.system.clan.vm.config'
|
||||||
|
]
|
||||||
|
),
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
).stdout
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def inspect(args: argparse.Namespace) -> None:
|
||||||
|
print(f"Creating VM for {args.machine}")
|
||||||
|
machine = args.machine
|
||||||
|
print(get_vm_inspect_info(machine))
|
||||||
|
|
||||||
|
|
||||||
|
def register_inspect_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
|
parser.add_argument("machine", type=str)
|
||||||
|
parser.set_defaults(func=inspect)
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
, nixpkgs
|
, nixpkgs
|
||||||
, makeDesktopItem
|
, makeDesktopItem
|
||||||
, copyDesktopItems
|
, copyDesktopItems
|
||||||
|
, qemu
|
||||||
|
, gnupg
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ let
|
|||||||
rsync
|
rsync
|
||||||
sops
|
sops
|
||||||
git
|
git
|
||||||
|
qemu
|
||||||
];
|
];
|
||||||
|
|
||||||
runtimeDependenciesAsSet = builtins.listToAttrs (builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies);
|
runtimeDependenciesAsSet = builtins.listToAttrs (builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies);
|
||||||
|
|||||||
Reference in New Issue
Block a user