This commit is contained in:
despiegk 2025-07-14 13:53:01 +04:00
parent 758e59e921
commit 7856fc0a4e
7 changed files with 367 additions and 0 deletions

View File

@ -23,6 +23,7 @@ members = [
"zinit_client",
"process",
"virt",
"zos",
"postgresclient",
"kubernetes",
"rhai",

37
zos/Cargo.toml Normal file
View File

@ -0,0 +1,37 @@
[package]
name = "zos-slicer"
version = "0.1.0"
edition = "2021"
authors = ["PlanetFirst <info@incubaid.com>"]
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" }

43
zos/src/slicer/cli.rs Normal file
View File

@ -0,0 +1,43 @@
use clap::{Parser, Subcommand};
#[derive(Debug, Parser)]
#[command(name = "talos-slicer")]
#[command(author = "PlanetFirst <info@incubaid.com>")]
#[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,
},
}

28
zos/src/slicer/error.rs Normal file
View File

@ -0,0 +1,28 @@
use thiserror::Error;
/// A specialized `Result` type for slicer operations.
pub type Result<T> = std::result::Result<T, SlicerError>;
/// 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),
}

37
zos/src/slicer/main.rs Normal file
View File

@ -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(())
}

View File

@ -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()
}
}

190
zos/src/specs.md Normal file
View File

@ -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 <vm_name> --slices <n_slices> --ipv6-range <ipv6_cidr>
````
### 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/<name>`
* 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/<name>/boot.qcow2`
* Clone from base image:
```bash
qemu-img create -f qcow2 -b /var/lib/images/talos-base.qcow2 /var/lib/talos-slicer/<name>/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/<name>/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-<name>`).
* 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/<name>/machineconfig.yaml`
Example `machineconfig.yaml` for a multi-homed setup:
```yaml
machine:
network:
interfaces:
- interface: eth0
addresses:
- <ipv6_address>/64
- interface: eth1
dhcp: true
hostname: <name>
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/<name>/boot.qcow2`
* Virtiofs mount: `/mnt/btrfs/vms/<name>` with tag `vmdata`
* Two network interfaces:
1. `mycelium-<name>` for IPv6
2. `nat-<name>` for IPv4 (NAT)
Command pattern:
```bash
cloud-hypervisor \
--kernel /boot/vmlinux-talos \
--disk path=/var/lib/talos-slicer/<name>/boot.qcow2 \
--fs tag=vmdata,path=/mnt/btrfs/vms/<name> \
--cpus <vcpus> \
--memory size=<ram_mb>M \
--net "tap=mycelium-<name>,ip=<ipv6>/64" \
--net "tap=nat-<name>"
```
---
## Directory Structure
```text
/var/lib/images/
└── talos-base.qcow2 # Immutable base image
/var/lib/talos-slicer/
└── <name>/
├── boot.qcow2 # VM-specific boot disk
├── machineconfig.yaml
└── ipv6.txt
/mnt/btrfs/vms/
└── <name>/ # 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