...
This commit is contained in:
parent
758e59e921
commit
7856fc0a4e
@ -23,6 +23,7 @@ members = [
|
||||
"zinit_client",
|
||||
"process",
|
||||
"virt",
|
||||
"zos",
|
||||
"postgresclient",
|
||||
"kubernetes",
|
||||
"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