...
This commit is contained in:
parent
758e59e921
commit
7856fc0a4e
@ -23,6 +23,7 @@ members = [
|
|||||||
"zinit_client",
|
"zinit_client",
|
||||||
"process",
|
"process",
|
||||||
"virt",
|
"virt",
|
||||||
|
"zos",
|
||||||
"postgresclient",
|
"postgresclient",
|
||||||
"kubernetes",
|
"kubernetes",
|
||||||
"rhai",
|
"rhai",
|
||||||
|
37
zos/Cargo.toml
Normal file
37
zos/Cargo.toml
Normal 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
43
zos/src/slicer/cli.rs
Normal 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
28
zos/src/slicer/error.rs
Normal 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
37
zos/src/slicer/main.rs
Normal 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(())
|
||||||
|
}
|
31
zos/src/slicer/resource.rs
Normal file
31
zos/src/slicer/resource.rs
Normal 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
190
zos/src/specs.md
Normal 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
|
Loading…
Reference in New Issue
Block a user