From 7856fc0a4e397d82e42108cd85d66f4027284f81 Mon Sep 17 00:00:00 2001 From: despiegk Date: Mon, 14 Jul 2025 13:53:01 +0400 Subject: [PATCH] ... --- Cargo.toml | 1 + zos/Cargo.toml | 37 ++++++++ zos/src/slicer/cli.rs | 43 +++++++++ zos/src/slicer/error.rs | 28 ++++++ zos/src/slicer/main.rs | 37 ++++++++ zos/src/slicer/resource.rs | 31 ++++++ zos/src/specs.md | 190 +++++++++++++++++++++++++++++++++++++ 7 files changed, 367 insertions(+) create mode 100644 zos/Cargo.toml create mode 100644 zos/src/slicer/cli.rs create mode 100644 zos/src/slicer/error.rs create mode 100644 zos/src/slicer/main.rs create mode 100644 zos/src/slicer/resource.rs create mode 100644 zos/src/specs.md diff --git a/Cargo.toml b/Cargo.toml index d588b4e..89dad2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "zinit_client", "process", "virt", + "zos", "postgresclient", "kubernetes", "rhai", diff --git a/zos/Cargo.toml b/zos/Cargo.toml new file mode 100644 index 0000000..828bf24 --- /dev/null +++ b/zos/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "zos-slicer" +version = "0.1.0" +edition = "2021" +authors = ["PlanetFirst "] +description = "ZOS Talos Slicer - VM provisioning tool for Talos OS with Cloud Hypervisor" + +[[bin]] +name = "talos-slicer" +path = "src/main.rs" + +[dependencies] +# CLI and argument parsing +clap = { version = "4.0", features = ["derive"] } + +# System information +sysinfo = "0.30" + +# Network utilities +ipnetwork = "0.20" + +# Error handling +anyhow = "1.0" +thiserror = "2.0" + +# Serialization for Talos config +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_yaml = "0.9" + +# UUID generation +uuid = { version = "1.0", features = ["v4"] } + +# SAL dependencies (relative paths) +sal-os = { path = "../os" } +sal-process = { path = "../process" } +sal-net = { path = "../net" } \ No newline at end of file diff --git a/zos/src/slicer/cli.rs b/zos/src/slicer/cli.rs new file mode 100644 index 0000000..4656ac7 --- /dev/null +++ b/zos/src/slicer/cli.rs @@ -0,0 +1,43 @@ +use clap::{Parser, Subcommand}; + +#[derive(Debug, Parser)] +#[command(name = "talos-slicer")] +#[command(author = "PlanetFirst ")] +#[command(version = "0.1.0")] +#[command(about = "ZOS Talos Slicer - VM provisioning tool for Talos OS with Cloud Hypervisor", long_about = None)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Debug, Subcommand)] +pub enum Commands { + /// Create a new Talos VM + Create { + /// Name of the VM + #[arg(short, long)] + name: String, + + /// Number of vCPUs + #[arg(long, default_value_t = 1)] + cpus: u8, + + /// Memory size in MB + #[arg(long, default_value_t = 512)] + memory: u32, + }, + /// Delete a Talos VM + Delete { + /// Name of the VM to delete + #[arg(short, long)] + name: String, + }, + /// List all Talos VMs + List, + /// Get information about a specific Talos VM + Info { + /// Name of the VM + #[arg(short, long)] + name: String, + }, +} \ No newline at end of file diff --git a/zos/src/slicer/error.rs b/zos/src/slicer/error.rs new file mode 100644 index 0000000..5d72fb3 --- /dev/null +++ b/zos/src/slicer/error.rs @@ -0,0 +1,28 @@ +use thiserror::Error; + +/// A specialized `Result` type for slicer operations. +pub type Result = std::result::Result; + +/// The error type for slicer operations. +#[derive(Debug, Error)] +pub enum SlicerError { + /// Errors related to configuration. + #[error("configuration error: {0}")] + Config(String), + + /// Errors related to virtual machine management. + #[error("vm error: {0}")] + Vm(String), + + /// Errors related to resource management. + #[error("resource error: {0}")] + Resource(String), + + /// Errors originating from I/O operations. + #[error("io error: {0}")] + Io(#[from] std::io::Error), + + /// Errors from the SAL OS library. + #[error("os error: {0}")] + SalOs(#[from] sal_os::fs::FsError), +} \ No newline at end of file diff --git a/zos/src/slicer/main.rs b/zos/src/slicer/main.rs new file mode 100644 index 0000000..1dcc256 --- /dev/null +++ b/zos/src/slicer/main.rs @@ -0,0 +1,37 @@ +use clap::Parser; + +mod cli; +mod error; +mod resource; + +use cli::{Cli, Commands}; +use error::Result; + +fn main() -> Result<()> { + let cli = Cli::parse(); + + match cli.command { + Commands::Create { name, cpus, memory } => { + println!("Creating VM '{}' with {} CPUs and {}MB memory", name, cpus, memory); + // Placeholder for create logic + } + Commands::Delete { name } => { + println!("Deleting VM '{}'", name); + // Placeholder for delete logic + } + Commands::List => { + let resources = resource::Resources::new(); + println!("Detected System Resources:"); + println!(" - CPU Cores: {}", resources.cpu_cores); + println!(" - Total Memory: {} MB", resources.total_memory_mb); + println!("\nListing all VMs..."); + // Placeholder for list logic + } + Commands::Info { name } => { + println!("Getting info for VM '{}'", name); + // Placeholder for info logic + } + } + + Ok(()) +} \ No newline at end of file diff --git a/zos/src/slicer/resource.rs b/zos/src/slicer/resource.rs new file mode 100644 index 0000000..7f59bd3 --- /dev/null +++ b/zos/src/slicer/resource.rs @@ -0,0 +1,31 @@ +use sysinfo::System; + +/// A struct to hold system resource information. +pub struct Resources { + /// Total memory in megabytes. + pub total_memory_mb: u64, + /// Number of CPU cores. + pub cpu_cores: usize, +} + +impl Resources { + /// Detects the system's resources. + pub fn new() -> Self { + let mut sys = System::new(); + sys.refresh_all(); + + let total_memory_mb = sys.total_memory() / 1024 / 1024; + let cpu_cores = sys.cpus().len(); + + Self { + total_memory_mb, + cpu_cores, + } + } +} + +impl Default for Resources { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file diff --git a/zos/src/specs.md b/zos/src/specs.md new file mode 100644 index 0000000..2c9e892 --- /dev/null +++ b/zos/src/specs.md @@ -0,0 +1,190 @@ + +## Goal + +Build a Rust CLI tool called `talos-slicer` that provisions a single Kubernetes VM using Talos OS with: + +- An **immutable boot disk** cloned from a QCOW2 base image +- **Persistent storage** provisioned via a Btrfs subvolume +- Dynamic allocation of **RAM**, **vCPU**, and **disk** based on requested slices +- **Multi-homed networking**: + - **IPv6** for Mycelium network connectivity via a TUN interface. + - **IPv4** for access to the outside world via a NATed interface. +- VM launched via **Cloud Hypervisor** + +--- + +## CLI Interface + +```bash +talos-slicer --name --slices --ipv6-range +```` + +### Parameters + +| Flag | Type | Description | +| -------------- | ------ | --------------------------------------- | +| `--name` | String | Unique name of the VM | +| `--slices` | u8 | Number of slices to allocate (2GB each) | +| `--ipv6-range` | String | IPv6 /64 subnet (e.g. `2a02:abcd::/64`) | + +--- + +## Assumptions + +* A Talos base image exists at `/var/lib/images/talos-base.qcow2` +* Btrfs is mounted at `/mnt/btrfs` with sufficient capacity +* Cloud Hypervisor and Mycelium are installed and accessible +* Talos kernel binary is located at `/boot/vmlinux-talos` + +--- + +## Kubernetes VM Resource Allocation + +### Memory + +* Detect total system RAM +* Reserve 1 GB if RAM < 32 GB, otherwise 2 GB +* Remaining RAM / 2 = max slices +* Validate `--slices ≤ max slices` +* Allocate `slices × 2 GB` RAM to the VM + +### CPU + +* Detect total logical CPU cores +* Allow 4x oversubscription +* vCPU = floor(`(total_cores × 4) / max_slices × requested_slices`) + +### Disk (Btrfs) + +* Calculate usable disk space: `total_capacity - 2GB` +* Compute `disk_per_slice = usable / max_slices` +* Create Btrfs subvolume at `/mnt/btrfs/vms/` +* Allocate `requested_slices × disk_per_slice` for that subvolume +* Expose subvolume to VM via `virtiofs` + +--- + +## Boot Disk + +* Create VM boot disk at `/var/lib/talos-slicer//boot.qcow2` +* Clone from base image: + + ```bash + qemu-img create -f qcow2 -b /var/lib/images/talos-base.qcow2 /var/lib/talos-slicer//boot.qcow2 + ``` + +--- + +## Networking + +The VM will be multi-homed, with two network interfaces. + +### Can Talos be made multi-homed? + +Yes, Talos can be configured with multiple network interfaces. This is achieved by specifying multiple network interfaces in the `machineconfig.yaml`. Talos will detect them on boot and configure them as specified. + +### Interface 1: IPv6 (Mycelium) + +This interface connects the VM to the Mycelium mesh network. + +* Network connectivity via a **Mycelium-managed TUN interface**. +* Parse the `--ipv6-range` as a `/64` CIDR. +* Assign a unique static IPv6 address (e.g., `::10 + index`). +* Save to `/var/lib/talos-slicer//ipv6.txt`. + +### Interface 2: IPv4 (NAT) + +This interface provides the VM with outbound internet access. + +* A dedicated tap device will be created for the VM (`nat-`). +* The host is responsible for bridging this tap device and applying NAT rules to provide internet connectivity. +* The VM will be configured to use DHCP to get an IP address on this interface. Talos supports DHCP on network interfaces by default if not configured statically. + +--- + +## Talos Config + +* Generate `machineconfig.yaml` to configure both network interfaces. +* Save config to `/var/lib/talos-slicer//machineconfig.yaml` + +Example `machineconfig.yaml` for a multi-homed setup: + +```yaml +machine: + network: + interfaces: + - interface: eth0 + addresses: + - /64 + - interface: eth1 + dhcp: true + hostname: +install: + extraData: + - path: /mnt/data + source: virtiofs + type: virtiofs +``` + +* The config defines `eth0` for the Mycelium IPv6 network and `eth1` for the NATed IPv4 network. +* `eth1` will be configured via DHCP. +* If `virtiofs` is used, it will be configured as a data mount. + +--- + +## Cloud Hypervisor Launch + +Launch VM with: + +* Boot disk: `/var/lib/talos-slicer//boot.qcow2` +* Virtiofs mount: `/mnt/btrfs/vms/` with tag `vmdata` +* Two network interfaces: + 1. `mycelium-` for IPv6 + 2. `nat-` for IPv4 (NAT) + +Command pattern: + +```bash +cloud-hypervisor \ + --kernel /boot/vmlinux-talos \ + --disk path=/var/lib/talos-slicer//boot.qcow2 \ + --fs tag=vmdata,path=/mnt/btrfs/vms/ \ + --cpus \ + --memory size=M \ + --net "tap=mycelium-,ip=/64" \ + --net "tap=nat-" +``` + +--- + +## Directory Structure + +```text +/var/lib/images/ + └── talos-base.qcow2 # Immutable base image + +/var/lib/talos-slicer/ + └── / + ├── boot.qcow2 # VM-specific boot disk + ├── machineconfig.yaml + └── ipv6.txt + +/mnt/btrfs/vms/ + └── / # Persistent storage subvolume + └── ... # VM data (etcd, kubelet, etc.) +``` + +## Remarks + +- if base image missing then download from Internet +- if Btrfs subvol/mount missing then create it + +## Error Handling + +* Exit if: + + * Not enough available slices + * Invalid IPv6 subnet, get from the tun adaptor + * Required tap devices (Mycelium, NAT) can't be created + * Btrfs mount not available + * Failure to set up NAT on the host