This commit is contained in:
44
.github/workflows/test.yaml
vendored
Normal file
44
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: Rust CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Rust
|
||||||
|
uses: actions/setup-rust@v2
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
|
||||||
|
- name: Cache Cargo registry
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-registry-
|
||||||
|
|
||||||
|
- name: Cache Cargo build
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-build-
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --verbose
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --verbose
|
||||||
51
.gitignore
vendored
Executable file
51
.gitignore
vendored
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer,react
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=rust,rust-analyzer,react
|
||||||
|
|
||||||
|
### react ###
|
||||||
|
.DS_*
|
||||||
|
*.log
|
||||||
|
logs
|
||||||
|
**/*.backup.*
|
||||||
|
**/*.back.*
|
||||||
|
|
||||||
|
.env
|
||||||
|
**/.env
|
||||||
|
.env.*
|
||||||
|
**/.env.*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
*.sublime*
|
||||||
|
|
||||||
|
psd
|
||||||
|
thumb
|
||||||
|
sketch
|
||||||
|
|
||||||
|
### Rust ###
|
||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
debug/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
### rust-analyzer ###
|
||||||
|
# Can be generated by other build systems other than cargo (ex: bazelbuild/rust_rules)
|
||||||
|
rust-project.json
|
||||||
|
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer,react
|
||||||
|
|
||||||
|
.venv
|
||||||
|
|
||||||
|
build
|
||||||
|
.VSCodeCounter
|
||||||
3
agent/Cargo.toml
Normal file
3
agent/Cargo.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
|
members = ["crates/vm"]
|
||||||
7
agent/crates/vm/Cargo.toml
Normal file
7
agent/crates/vm/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "dlx-vm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "2.0.14"
|
||||||
9
agent/crates/vm/src/error.rs
Normal file
9
agent/crates/vm/src/error.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::qemu::error::QemuError;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Error)]
|
||||||
|
pub enum VmError {
|
||||||
|
#[error("QEMU error")]
|
||||||
|
Qemu(#[from] QemuError),
|
||||||
|
}
|
||||||
4
agent/crates/vm/src/lib.rs
Normal file
4
agent/crates/vm/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
mod error;
|
||||||
|
mod model;
|
||||||
|
mod qemu;
|
||||||
|
mod service;
|
||||||
3
agent/crates/vm/src/model.rs
Normal file
3
agent/crates/vm/src/model.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod disk;
|
||||||
|
pub mod net;
|
||||||
|
pub mod vm;
|
||||||
7
agent/crates/vm/src/model/disk.rs
Normal file
7
agent/crates/vm/src/model/disk.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Disk {
|
||||||
|
pub file: String,
|
||||||
|
pub index: u32,
|
||||||
|
pub format: Option<String>,
|
||||||
|
pub media: String,
|
||||||
|
}
|
||||||
2
agent/crates/vm/src/model/net.rs
Normal file
2
agent/crates/vm/src/model/net.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct VmNet(pub String);
|
||||||
15
agent/crates/vm/src/model/vm.rs
Normal file
15
agent/crates/vm/src/model/vm.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use crate::model::{disk::Disk, net::VmNet};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct MemoryMB(pub u32);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct MachineAccel(pub String);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct CloudInitVM {
|
||||||
|
pub mem: MemoryMB,
|
||||||
|
pub nets: Vec<VmNet>,
|
||||||
|
pub disks: Vec<Disk>,
|
||||||
|
pub machine: MachineAccel,
|
||||||
|
}
|
||||||
4
agent/crates/vm/src/qemu.rs
Normal file
4
agent/crates/vm/src/qemu.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod error;
|
||||||
|
pub mod param;
|
||||||
|
pub mod runner;
|
||||||
7
agent/crates/vm/src/qemu/config.rs
Normal file
7
agent/crates/vm/src/qemu/config.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::qemu::param::QemuParam;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct QemuConfig {
|
||||||
|
pub executable: String,
|
||||||
|
pub param: QemuParam,
|
||||||
|
}
|
||||||
7
agent/crates/vm/src/qemu/error.rs
Normal file
7
agent/crates/vm/src/qemu/error.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Error)]
|
||||||
|
pub enum QemuError {
|
||||||
|
#[error("unknown qemu error")]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
158
agent/crates/vm/src/qemu/param.rs
Normal file
158
agent/crates/vm/src/qemu/param.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
use crate::model::{
|
||||||
|
disk::Disk,
|
||||||
|
net::VmNet,
|
||||||
|
vm::{CloudInitVM, MachineAccel, MemoryMB},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct QemuParam(pub String);
|
||||||
|
|
||||||
|
pub trait IntoQemuParam {
|
||||||
|
fn into_qemu_param(self) -> QemuParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoQemuParam for MachineAccel {
|
||||||
|
fn into_qemu_param(self) -> QemuParam {
|
||||||
|
QemuParam(format!("-machine accel={}", self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoQemuParam for MemoryMB {
|
||||||
|
fn into_qemu_param(self) -> QemuParam {
|
||||||
|
QemuParam(format!("-m {}", self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoQemuParam for VmNet {
|
||||||
|
fn into_qemu_param(self) -> QemuParam {
|
||||||
|
QemuParam(format!("-net {}", self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoQemuParam for Disk {
|
||||||
|
fn into_qemu_param(self) -> QemuParam {
|
||||||
|
let mut v: Vec<String> = vec![];
|
||||||
|
v.push(format!("file={}", self.file));
|
||||||
|
v.push(format!("index={}", self.index));
|
||||||
|
if let Some(format) = self.format {
|
||||||
|
v.push(format!("format={}", format));
|
||||||
|
}
|
||||||
|
v.push(format!("media={}", self.media));
|
||||||
|
|
||||||
|
let v = v.join(",");
|
||||||
|
|
||||||
|
QemuParam(format!("-drive {}", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoQemuParam for CloudInitVM {
|
||||||
|
fn into_qemu_param(self) -> QemuParam {
|
||||||
|
let mut params: Vec<QemuParam> = vec![];
|
||||||
|
|
||||||
|
params.push(self.mem.into_qemu_param());
|
||||||
|
|
||||||
|
let nets: Vec<QemuParam> = self
|
||||||
|
.nets
|
||||||
|
.into_iter()
|
||||||
|
.map(IntoQemuParam::into_qemu_param)
|
||||||
|
.collect();
|
||||||
|
params.extend_from_slice(&nets);
|
||||||
|
|
||||||
|
let disks: Vec<QemuParam> = self
|
||||||
|
.disks
|
||||||
|
.into_iter()
|
||||||
|
.map(IntoQemuParam::into_qemu_param)
|
||||||
|
.collect();
|
||||||
|
params.extend_from_slice(&disks);
|
||||||
|
|
||||||
|
params.push(self.machine.into_qemu_param());
|
||||||
|
|
||||||
|
let param = params
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| e.0)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
QemuParam(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
model::{
|
||||||
|
disk::Disk,
|
||||||
|
net::VmNet,
|
||||||
|
vm::{CloudInitVM, MachineAccel, MemoryMB},
|
||||||
|
},
|
||||||
|
qemu::param::{IntoQemuParam, QemuParam},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_qemu_param_disk_format() {
|
||||||
|
let disk = Disk {
|
||||||
|
file: "f".into(),
|
||||||
|
index: 1,
|
||||||
|
format: Some("123".into()),
|
||||||
|
media: "m".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let param = disk.into_qemu_param();
|
||||||
|
assert_eq!(
|
||||||
|
param,
|
||||||
|
QemuParam("-drive file=f,index=1,format=123,media=m".into())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_qemu_param_disk_no_format() {
|
||||||
|
let disk = Disk {
|
||||||
|
file: "f".into(),
|
||||||
|
index: 1,
|
||||||
|
format: None,
|
||||||
|
media: "m".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let param = disk.into_qemu_param();
|
||||||
|
assert_eq!(param, QemuParam("-drive file=f,index=1,media=m".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_qemu_param_mem() {
|
||||||
|
let param = MemoryMB(1024).into_qemu_param();
|
||||||
|
assert_eq!(param, QemuParam("-m 1024".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_qemu_param_machine() {
|
||||||
|
let param = MachineAccel("kvm:tcg".into()).into_qemu_param();
|
||||||
|
assert_eq!(param, QemuParam("-machine accel=kvm:tcg".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_qemu_param_net() {
|
||||||
|
let param = VmNet("nic".into()).into_qemu_param();
|
||||||
|
assert_eq!(param, QemuParam("-net nic".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_qemu_param_vm() {
|
||||||
|
let vm = CloudInitVM {
|
||||||
|
mem: MemoryMB(1024),
|
||||||
|
nets: vec![VmNet("1".into()), VmNet("2".into())],
|
||||||
|
disks: vec![Disk {
|
||||||
|
file: "f".into(),
|
||||||
|
index: 1,
|
||||||
|
format: None,
|
||||||
|
media: "m".into(),
|
||||||
|
}],
|
||||||
|
machine: MachineAccel("kvm:tcg".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let param = vm.into_qemu_param();
|
||||||
|
assert_eq!(
|
||||||
|
param.0,
|
||||||
|
"-m 1024 -net 1 -net 2 -drive file=f,index=1,media=m -machine accel=kvm:tcg"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
agent/crates/vm/src/qemu/runner.rs
Normal file
15
agent/crates/vm/src/qemu/runner.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use crate::qemu::config::QemuConfig;
|
||||||
|
|
||||||
|
pub fn run_qemu(config: &QemuConfig) {
|
||||||
|
let args: Vec<String> = config
|
||||||
|
.param
|
||||||
|
.clone()
|
||||||
|
.0
|
||||||
|
.split(" ")
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let handle = Command::new(&config.executable).args(&args).spawn();
|
||||||
|
}
|
||||||
1
agent/crates/vm/src/service.rs
Normal file
1
agent/crates/vm/src/service.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod runner;
|
||||||
2
agent/crates/vm/src/service/runner.rs
Normal file
2
agent/crates/vm/src/service/runner.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub trait RunnerService {
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user