diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 134a717..ef68bd7 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -20,6 +20,8 @@ linux-loader = { version = "0.11.0", features = ["bzimage", "elf"] } log = "0.4.20" nix = { version = "0.28.0", features = ["term"] } openpty = "0.2.0" +prost = "0.11" +tonic = "0.9" tracing = "0.1.40" tracing-subscriber = "0.3.18" virtio-bindings = "0.2.2" @@ -27,3 +29,7 @@ vm-device = "0.1.0" vm-memory = { version = "0.14.0", features = ["backend-mmap"] } vm-superio = "0.7.0" vmm-sys-util = "0.12.1" +tokio = { version= "1.37.0", features= ["full"]} + +[build-dependencies] +tonic-build = "0.9" \ No newline at end of file diff --git a/src/vmm/build.rs b/src/vmm/build.rs new file mode 100644 index 0000000..c53d705 --- /dev/null +++ b/src/vmm/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("../../proto/vmm.proto")?; + Ok(()) +} diff --git a/src/vmm/src/args.rs b/src/vmm/src/args.rs index fcfca2d..d4e9bcc 100644 --- a/src/vmm/src/args.rs +++ b/src/vmm/src/args.rs @@ -5,16 +5,31 @@ use clap::Parser; use clap_verbosity_flag::{InfoLevel, Verbosity}; use tracing::level_filters; -/// The Virtual Machine Manager for the Cloudlet serverless runtime. #[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] +#[command(version, about)] +pub struct CliArgs { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Parser, Debug)] +pub enum Commands { + #[command(about = "Run a VMM instance.")] + Cli(CliArguments), + #[command(about = "Run a GRPC server listening for incoming requests.")] + Grpc, +} + +/// Run a VMM instance. +#[derive(Parser, Debug)] +#[command(author, version, about)] pub struct CliArguments { /// Path to the image of the Linux kernel to boot. - #[arg(short, long, env)] + #[arg(short, long, env, required = true)] pub kernel: PathBuf, /// Path to the cpio archive to use as the initramfs. - #[arg(short, long, env)] + #[arg(short, long, env, required = true)] pub initramfs: Option, /// Number of virtual CPUs assigned to the guest. @@ -26,11 +41,11 @@ pub struct CliArguments { pub memory: u32, /// IPv4 address of the host tap interface. - #[clap(long, env)] + #[clap(long, env, required = true)] pub network_host_ip: Ipv4Addr, /// Subnet mask of the host tap interface. - #[clap(long, env)] + #[clap(long, env, required = true)] pub network_host_netmask: Ipv4Addr, /// Verbosity level. diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 5a7ca06..f5df684 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -1 +1,9 @@ pub mod core; +pub mod service; + +#[derive(Debug)] +pub enum VmmErrors { + VmmNew(core::Error), + VmmConfigure(core::Error), + VmmRun(core::Error), +} diff --git a/src/vmm/src/main.rs b/src/vmm/src/main.rs index c0f008d..3662646 100644 --- a/src/vmm/src/main.rs +++ b/src/vmm/src/main.rs @@ -1,24 +1,18 @@ -use crate::args::CliArguments; +use crate::args::{CliArgs, Commands}; use clap::Parser; +use tonic::transport::Server; use tracing::info; -use vmm::core::{self, vmm::VMM}; - +use vmm::{ + core::vmm::VMM, + service::{vmmorchestrator, VmmService}, + VmmErrors, +}; mod args; -#[derive(Debug)] -pub enum Error { - VmmNew(core::Error), - VmmConfigure(core::Error), - VmmRun(core::Error), -} - -/// The application entry point. -fn main() -> Result<(), Error> { +#[tokio::main] +async fn main() -> Result<(), Box> { // Parse the configuration and configure logger verbosity - let args = CliArguments::parse(); - tracing_subscriber::fmt() - .with_max_level(args.convert_log_to_tracing()) - .init(); + let args = CliArgs::parse(); info!( app_name = env!("CARGO_PKG_NAME"), @@ -26,15 +20,41 @@ fn main() -> Result<(), Error> { "Starting application", ); - // Create a new VMM - let mut vmm = - VMM::new(args.network_host_ip, args.network_host_netmask).map_err(Error::VmmNew)?; - - vmm.configure(args.cpus, args.memory, &args.kernel, &args.initramfs) - .map_err(Error::VmmConfigure)?; - - // Run the VMM - vmm.run().map_err(Error::VmmRun)?; + let addr = "[::1]:50051".parse().unwrap(); + let vmm_service = VmmService; + + // check if the args is grpc or command + match args.command { + Commands::Grpc => { + Server::builder() + .add_service(vmmorchestrator::vmm_service_server::VmmServiceServer::new( + vmm_service, + )) + .serve(addr) + .await?; + } + Commands::Cli(cli_args) => { + tracing_subscriber::fmt() + .with_max_level(cli_args.convert_log_to_tracing()) + .init(); + // Create a new VMM + let mut vmm = VMM::new(cli_args.network_host_ip, cli_args.network_host_netmask) + .map_err(VmmErrors::VmmNew) + .unwrap(); + + vmm.configure( + cli_args.cpus, + cli_args.memory, + &cli_args.kernel, + &cli_args.initramfs, + ) + .map_err(VmmErrors::VmmConfigure) + .unwrap(); + + // Run the VMM + vmm.run().map_err(VmmErrors::VmmRun).unwrap(); + } + } Ok(()) } diff --git a/src/vmm/src/service.rs b/src/vmm/src/service.rs new file mode 100644 index 0000000..bd1dfac --- /dev/null +++ b/src/vmm/src/service.rs @@ -0,0 +1,82 @@ +use self::vmmorchestrator::{ + vmm_service_server::VmmService as VmmServiceTrait, RunVmmRequest, RunVmmResponse, +}; +use crate::core::vmm::VMM; +use crate::VmmErrors; +use std::{ + convert::From, + net::Ipv4Addr, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; +use tonic::{Request, Response, Status}; +use tracing::{error, info}; + +pub mod vmmorchestrator { + tonic::include_proto!("vmmorchestrator"); +} + +// Implement the From trait for VmmErrors into Status +impl From for Status { + fn from(error: VmmErrors) -> Self { + // You can create a custom Status variant based on the error + match error { + VmmErrors::VmmNew(_) => Status::internal("Error creating VMM"), + VmmErrors::VmmConfigure(_) => Status::internal("Error configuring VMM"), + VmmErrors::VmmRun(_) => Status::internal("Error running VMM"), + } + } +} + +#[derive(Default)] +pub struct VmmService; + +#[tonic::async_trait] +impl VmmServiceTrait for VmmService { + async fn run( + &self, + _request: Request, + ) -> Result, Status> { + let response = vmmorchestrator::RunVmmResponse {}; + + const HOST_IP: Ipv4Addr = Ipv4Addr::new(172, 29, 0, 1); + const HOST_NETMASK: Ipv4Addr = Ipv4Addr::new(255, 255, 0, 0); + + // Check if the kernel is on the system, else build it + if !Path::new("./tools/kernel/linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin") + .exists() + { + info!("Kernel not found, building kernel"); + // Execute the script using sh and capture output and error streams + let output = Command::new("sh") + .arg("./tools/kernel/mkkernel.sh") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .expect("Failed to execute the kernel build script"); + + // Print output and error streams + error!("Script output: {}", String::from_utf8_lossy(&output.stdout)); + error!("Script errors: {}", String::from_utf8_lossy(&output.stderr)); + }; + + let kernel_path = &Path::new( + "./tools/kernel/linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin", + ); + let mut initramfs_path: PathBuf = PathBuf::new(); + + // Todo - Check if the initramfs for the specified language is on the system, else build it + initramfs_path.push("./tools/rootfs/initramfs.img"); + + // // Create a new VMM + let mut vmm = VMM::new(HOST_IP, HOST_NETMASK).map_err(VmmErrors::VmmNew)?; + + // Configure the VMM parameters might need to be calculated rather than hardcoded + vmm.configure(1, 512, kernel_path, &Some(initramfs_path)) + .map_err(VmmErrors::VmmConfigure)?; + // Run the VMM + vmm.run().map_err(VmmErrors::VmmRun)?; + + Ok(Response::new(response)) + } +}