Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization linux microvm firecracker
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

add more command line options: --bridge, --tap, --mac-address, --api-socket

+172 -67
+1
Cargo.lock
··· 169 169 version = "0.1.0" 170 170 dependencies = [ 171 171 "anyhow", 172 + "firecracker-vm", 172 173 "libc", 173 174 "owo-colors", 174 175 ]
+8
crates/fire-config/src/lib.rs
··· 12 12 pub vmlinux: Option<String>, 13 13 pub rootfs: Option<String>, 14 14 pub boot_args: Option<String>, 15 + pub bridge: Option<String>, 16 + pub tap: Option<String>, 17 + pub api_socket: Option<String>, 18 + pub mac: Option<String>, 15 19 } 16 20 17 21 #[derive(Debug, Serialize, Deserialize)] ··· 30 34 vmlinux: None, 31 35 rootfs: None, 32 36 boot_args: None, 37 + bridge: None, 38 + tap: None, 39 + api_socket: None, 40 + mac: None, 33 41 }, 34 42 } 35 43 }
+2 -2
crates/firecracker-prepare/src/lib.rs
··· 12 12 pub mod rootfs; 13 13 pub mod ssh; 14 14 15 - const BBRIDGE_IP: &str = "172.16.0.1"; 15 + const BRIDGE_IP: &str = "172.16.0.1"; 16 16 17 17 #[derive(Clone, Copy, PartialEq, Serialize, Deserialize, Debug)] 18 18 pub enum Distro { ··· 133 133 "-c", 134 134 &format!( 135 135 "echo 'nameserver {}' >> {}/etc/resolv.conf", 136 - BBRIDGE_IP, minirootfs 136 + BRIDGE_IP, minirootfs 137 137 ), 138 138 ], 139 139 true,
+1
crates/firecracker-process/Cargo.toml
··· 10 10 anyhow = "1.0.98" 11 11 libc = "0.2.174" 12 12 owo-colors = "4.2.2" 13 + firecracker-vm = { path = "../firecracker-vm" }
+7 -8
crates/firecracker-process/src/lib.rs
··· 1 1 use anyhow::Result; 2 + use firecracker_vm::types::VmOptions; 2 3 3 4 use crate::command::{run_command, run_command_in_background}; 4 5 5 6 pub mod command; 6 7 7 - pub const FIRECRACKER_SOCKET: &str = "/tmp/firecracker.sock"; 8 - 9 - pub fn start() -> Result<()> { 10 - stop()?; 8 + pub fn start(config: &VmOptions) -> Result<()> { 9 + stop(config)?; 11 10 println!("[+] Starting Firecracker..."); 12 - run_command_in_background("firecracker", &["--api-sock", FIRECRACKER_SOCKET], true)?; 11 + run_command_in_background("firecracker", &["--api-sock", &config.api_socket], true)?; 13 12 Ok(()) 14 13 } 15 14 16 - pub fn stop() -> Result<()> { 15 + pub fn stop(config: &VmOptions) -> Result<()> { 17 16 if !is_running() { 18 17 println!("[!] Firecracker is not running."); 19 - run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 18 + run_command("rm", &["-rf", &config.api_socket], true)?; 20 19 return Ok(()); 21 20 } 22 21 run_command("killall", &["-s", "KILL", "firecracker"], true)?; 23 - run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 22 + run_command("rm", &["-rf", &config.api_socket], true)?; 24 23 println!("[+] Firecracker has been stopped."); 25 24 Ok(()) 26 25 }
+3 -2
crates/firecracker-up/src/cmd/down.rs
··· 1 1 use anyhow::Error; 2 + use firecracker_vm::types::VmOptions; 2 3 3 - pub fn down() -> Result<(), Error> { 4 - firecracker_process::stop()?; 4 + pub fn down(options: &VmOptions) -> Result<(), Error> { 5 + firecracker_process::stop(options)?; 5 6 Ok(()) 6 7 }
+3 -2
crates/firecracker-up/src/cmd/reset.rs
··· 1 1 use anyhow::Error; 2 + use firecracker_vm::types::VmOptions; 2 3 use glob::glob; 3 4 use owo_colors::OwoColorize; 4 5 5 6 use crate::cmd::down::down; 6 7 7 - pub fn reset() -> Result<(), Error> { 8 + pub fn reset(options: VmOptions) -> Result<(), Error> { 8 9 println!( 9 10 "Are you sure you want to reset? This will remove all ext4 files. Type '{}' to confirm:", 10 11 "yes".bright_green() ··· 20 21 return Ok(()); 21 22 } 22 23 23 - down()?; 24 + down(&options)?; 24 25 25 26 let app_dir = crate::config::get_config_dir()?; 26 27 let ext4_file = glob(format!("{}/*.ext4", app_dir).as_str())
+1 -1
crates/firecracker-up/src/cmd/up.rs
··· 15 15 Err(_) => options.clone(), 16 16 }; 17 17 18 - firecracker_process::start()?; 18 + firecracker_process::start(&options)?; 19 19 20 20 loop { 21 21 thread::sleep(std::time::Duration::from_secs(1));
+88 -5
crates/firecracker-up/src/main.rs
··· 1 1 use anyhow::Result; 2 2 use clap::{arg, Arg, Command}; 3 - use firecracker_vm::types::VmOptions; 3 + use firecracker_vm::{ 4 + constants::{BRIDGE_DEV, FC_MAC, FIRECRACKER_SOCKET, TAP_DEV}, 5 + types::VmOptions, 6 + }; 4 7 use owo_colors::OwoColorize; 5 8 6 9 use crate::cmd::{ ··· 41 44 .arg(arg!(--memory <m> "Memory size in MiB")) 42 45 .arg(arg!(--vmlinux <path> "Path to the kernel image")) 43 46 .arg(arg!(--rootfs <path> "Path to the root filesystem image")) 47 + .arg(arg!(--bridge <name> "Name of the bridge interface").default_value(BRIDGE_DEV)) 48 + .arg(arg!(--tap <name> "Name of the tap interface").default_value(TAP_DEV)) 49 + .arg( 50 + Arg::new("mac-address") 51 + .long("mac-address") 52 + .value_name("MAC") 53 + .default_value(FC_MAC) 54 + .help("MAC address for the network interface"), 55 + ) 56 + .arg( 57 + Arg::new("api-socket") 58 + .long("api-socket") 59 + .value_name("path") 60 + .default_value(FIRECRACKER_SOCKET) 61 + .help("Path to the Firecracker API socket"), 62 + ) 44 63 .arg( 45 64 Arg::new("boot-args") 46 65 .long("boot-args") ··· 49 68 ) 50 69 .about("Start Firecracker MicroVM"), 51 70 ) 52 - .subcommand(Command::new("down").about("Stop Firecracker MicroVM")) 71 + .subcommand( 72 + Command::new("down") 73 + .arg( 74 + Arg::new("api-socket") 75 + .long("api-socket") 76 + .value_name("path") 77 + .default_value(FIRECRACKER_SOCKET) 78 + .help("Path to the Firecracker API socket"), 79 + ) 80 + .about("Stop Firecracker MicroVM"), 81 + ) 53 82 .subcommand(Command::new("status").about("Check the status of Firecracker MicroVM")) 54 83 .subcommand( 55 84 Command::new("logs") ··· 62 91 .about("View the logs of the Firecracker MicroVM"), 63 92 ) 64 93 .subcommand(Command::new("ssh").about("SSH into the Firecracker MicroVM")) 65 - .subcommand(Command::new("reset").about("Reset the Firecracker MicroVM")) 94 + .subcommand( 95 + Command::new("reset") 96 + .arg( 97 + Arg::new("api-socket") 98 + .long("api-socket") 99 + .value_name("path") 100 + .default_value(FIRECRACKER_SOCKET) 101 + .help("Path to the Firecracker API socket"), 102 + ) 103 + .about("Reset the Firecracker MicroVM"), 104 + ) 66 105 .arg(arg!(--debian "Prepare Debian MicroVM").default_value("false")) 67 106 .arg(arg!(--alpine "Prepare Alpine MicroVM").default_value("false")) 68 107 .arg(arg!(--nixos "Prepare NixOS MicroVM").default_value("false")) ··· 71 110 .arg(arg!(--memory <m> "Memory size in MiB")) 72 111 .arg(arg!(--vmlinux <path> "Path to the kernel image")) 73 112 .arg(arg!(--rootfs <path> "Path to the root filesystem image")) 113 + .arg(arg!(--bridge <name> "Name of the bridge interface").default_value(BRIDGE_DEV)) 114 + .arg(arg!(--tap <name> "Name of the tap interface").default_value(TAP_DEV)) 115 + .arg( 116 + Arg::new("mac-address") 117 + .long("mac-address") 118 + .value_name("MAC") 119 + .default_value(FC_MAC) 120 + .help("MAC address for the network interface"), 121 + ) 122 + .arg( 123 + Arg::new("api-socket") 124 + .long("api-socket") 125 + .value_name("path") 126 + .default_value(FIRECRACKER_SOCKET) 127 + .help("Path to the Firecracker API socket"), 128 + ) 74 129 .arg( 75 130 Arg::new("boot-args") 76 131 .long("boot-args") ··· 96 151 let vmlinux = matches.get_one::<String>("vmlinux").cloned(); 97 152 let rootfs = matches.get_one::<String>("rootfs").cloned(); 98 153 let bootargs = matches.get_one::<String>("boot-args").cloned(); 154 + let bridge = args.get_one::<String>("bridge").cloned().unwrap(); 155 + let tap = args.get_one::<String>("tap").cloned().unwrap(); 156 + let api_socket = args.get_one::<String>("api-socket").cloned().unwrap(); 157 + let mac_address = args.get_one::<String>("mac-address").cloned().unwrap(); 99 158 let options = VmOptions { 100 159 debian: args.get_one::<bool>("debian").copied(), 101 160 alpine: args.get_one::<bool>("alpine").copied(), ··· 106 165 vmlinux, 107 166 rootfs, 108 167 bootargs, 168 + bridge, 169 + tap, 170 + api_socket, 171 + mac_address, 109 172 }; 110 173 up(options)? 111 174 } 112 - Some(("down", _)) => down()?, 175 + Some(("down", args)) => { 176 + let api_socket = args.get_one::<String>("api-socket").cloned().unwrap(); 177 + down(&VmOptions { 178 + api_socket, 179 + ..Default::default() 180 + })? 181 + } 113 182 Some(("status", _)) => status()?, 114 183 Some(("logs", args)) => { 115 184 let follow = args.get_one::<bool>("follow").copied().unwrap_or(false); 116 185 logs(follow)?; 117 186 } 118 187 Some(("ssh", _)) => ssh()?, 119 - Some(("reset", _)) => reset()?, 188 + Some(("reset", args)) => { 189 + let api_socket = args.get_one::<String>("api-socket").cloned().unwrap(); 190 + reset(VmOptions { 191 + api_socket, 192 + ..Default::default() 193 + })? 194 + } 120 195 _ => { 121 196 let debian = matches.get_one::<bool>("debian").copied().unwrap_or(false); 122 197 let alpine = matches.get_one::<bool>("alpine").copied().unwrap_or(false); ··· 133 208 let vmlinux = matches.get_one::<String>("vmlinux").cloned(); 134 209 let rootfs = matches.get_one::<String>("rootfs").cloned(); 135 210 let bootargs = matches.get_one::<String>("boot-args").cloned(); 211 + let bridge = matches.get_one::<String>("bridge").cloned().unwrap(); 212 + let tap = matches.get_one::<String>("tap").cloned().unwrap(); 213 + let api_socket = matches.get_one::<String>("api-socket").cloned().unwrap(); 214 + let mac_address = matches.get_one::<String>("mac-address").cloned().unwrap(); 136 215 137 216 let options = VmOptions { 138 217 debian: Some(debian), ··· 144 223 vmlinux, 145 224 rootfs, 146 225 bootargs, 226 + bridge, 227 + tap, 228 + api_socket, 229 + mac_address, 147 230 }; 148 231 up(options)? 149 232 }
+1 -1
crates/firecracker-vm/src/constants.rs
··· 1 1 pub const TAP_DEV: &str = "tap0"; 2 2 pub const BRIDGE_DEV: &str = "br0"; 3 - pub const API_SOCKET: &str = "/tmp/firecracker.sock"; 3 + pub const FIRECRACKER_SOCKET: &str = "/tmp/firecracker.sock"; 4 4 pub const BRIDGE_IP: &str = "172.16.0.1"; 5 5 pub const FC_MAC: &str = "06:00:AC:10:00:02"; 6 6 pub const MASK_SHORT: &str = "/30";
+3 -6
crates/firecracker-vm/src/dnsmasq.rs
··· 2 2 3 3 use anyhow::Error; 4 4 5 - use crate::{ 6 - command::run_command, 7 - constants::{BRIDGE_DEV, BRIDGE_IP, FC_MAC}, 8 - }; 5 + use crate::{command::run_command, constants::BRIDGE_IP, types::VmOptions}; 9 6 10 7 pub const DNSMASQ_CONFIG_PATH: &str = "/etc/dnsmasq.d/firecracker.conf"; 11 8 12 - pub fn setup_dnsmasq() -> Result<(), Error> { 9 + pub fn setup_dnsmasq(config: &VmOptions) -> Result<(), Error> { 13 10 println!("[+] Checking if DNSMasq is installed..."); 14 11 if !dnsmasq_is_installed()? { 15 12 println!("[✗] DNSMasq is not installed. Please install it first."); ··· 36 33 server=8.8.4.4 37 34 server=1.1.1.1 38 35 "#, 39 - BRIDGE_DEV, BRIDGE_IP, BRIDGE_IP, FC_MAC 36 + &config.bridge, BRIDGE_IP, BRIDGE_IP, &config.mac_address 40 37 ); 41 38 42 39 run_command(
+18 -19
crates/firecracker-vm/src/firecracker.rs
··· 1 - use crate::constants::{API_SOCKET, FC_MAC, TAP_DEV}; 2 1 use crate::types::VmOptions; 3 2 use anyhow::Result; 4 3 use firecracker_prepare::Distro; ··· 18 17 options: &VmOptions, 19 18 distro: Distro, 20 19 ) -> Result<()> { 21 - configure_logger(logfile)?; 20 + configure_logger(logfile, options)?; 22 21 setup_boot_source(kernel, arch, distro == Distro::NixOS, &options)?; 23 - setup_rootfs(rootfs)?; 24 - setup_network_interface()?; 25 - setup_vcpu_and_memory(options.vcpu, options.memory)?; 22 + setup_rootfs(rootfs, options)?; 23 + setup_network_interface(options)?; 24 + setup_vcpu_and_memory(options.vcpu, options.memory, &options.api_socket)?; 26 25 27 26 // Wait before starting instance 28 27 sleep(Duration::from_millis(15)); 29 28 30 - start_microvm()?; 29 + start_microvm(options)?; 31 30 32 31 // Wait for VM to boot 33 32 sleep(Duration::from_secs(2)); 34 33 Ok(()) 35 34 } 36 35 37 - fn configure_logger(logfile: &str) -> Result<()> { 36 + fn configure_logger(logfile: &str, options: &VmOptions) -> Result<()> { 38 37 println!("[+] Configuring logger..."); 39 38 let payload = json!({ 40 39 "log_path": logfile, ··· 49 48 "-X", 50 49 "PUT", 51 50 "--unix-socket", 52 - API_SOCKET, 51 + &options.api_socket, 53 52 "--data", 54 53 &payload.to_string(), 55 54 "http://localhost/logger", ··· 89 88 "-X", 90 89 "PUT", 91 90 "--unix-socket", 92 - API_SOCKET, 91 + &options.api_socket, 93 92 "--data", 94 93 &payload.to_string(), 95 94 "http://localhost/boot-source", ··· 99 98 Ok(()) 100 99 } 101 100 102 - fn setup_rootfs(rootfs: &str) -> Result<()> { 101 + fn setup_rootfs(rootfs: &str, options: &VmOptions) -> Result<()> { 103 102 println!("[+] Setting rootfs..."); 104 103 let payload = json!({ 105 104 "drive_id": "rootfs", ··· 114 113 "-X", 115 114 "PUT", 116 115 "--unix-socket", 117 - API_SOCKET, 116 + &options.api_socket, 118 117 "--data", 119 118 &payload.to_string(), 120 119 "http://localhost/drives/rootfs", ··· 124 123 Ok(()) 125 124 } 126 125 127 - fn setup_network_interface() -> Result<()> { 126 + fn setup_network_interface(options: &VmOptions) -> Result<()> { 128 127 println!("[+] Setting network interface..."); 129 128 let iface = "eth0"; 130 129 let payload = json!({ 131 130 "iface_id": iface, 132 - "guest_mac": FC_MAC, 133 - "host_dev_name": TAP_DEV 131 + "guest_mac": &options.mac_address, 132 + "host_dev_name": &options.tap 134 133 }); 135 134 136 135 println!("{}", payload.to_string()); ··· 141 140 "-X", 142 141 "PUT", 143 142 "--unix-socket", 144 - API_SOCKET, 143 + &options.api_socket, 145 144 "--data", 146 145 &payload.to_string(), 147 146 &format!("http://localhost/network-interfaces/{}", iface), ··· 151 150 Ok(()) 152 151 } 153 152 154 - fn start_microvm() -> Result<()> { 153 + fn start_microvm(options: &VmOptions) -> Result<()> { 155 154 println!("[+] Starting microVM..."); 156 155 let payload = json!({ 157 156 "action_type": "InstanceStart" ··· 163 162 "-X", 164 163 "PUT", 165 164 "--unix-socket", 166 - API_SOCKET, 165 + &options.api_socket, 167 166 "--data", 168 167 &payload.to_string(), 169 168 "http://localhost/actions", ··· 173 172 Ok(()) 174 173 } 175 174 176 - fn setup_vcpu_and_memory(n: u16, memory: u16) -> Result<()> { 175 + fn setup_vcpu_and_memory(n: u16, memory: u16, api_socket: &str) -> Result<()> { 177 176 println!("[+] Setting vCPU and memory..."); 178 177 let payload = json!({ 179 178 "vcpu_count": n, ··· 188 187 "-X", 189 188 "PUT", 190 189 "--unix-socket", 191 - API_SOCKET, 190 + api_socket, 192 191 "--data", 193 192 &payload.to_string(), 194 193 "http://localhost/machine-config",
+1 -1
crates/firecracker-vm/src/lib.rs
··· 75 75 .to_string(); 76 76 let arch = command::run_command("uname", &["-m"], false)?.stdout; 77 77 let arch = String::from_utf8_lossy(&arch).trim().to_string(); 78 - network::setup_network()?; 78 + network::setup_network(options)?; 79 79 80 80 firecracker::configure(&logfile, &kernel, &rootfs, &arch, &options, distro)?; 81 81
+25 -20
crates/firecracker-vm/src/network.rs
··· 1 1 use crate::{ 2 - constants::{BRIDGE_DEV, BRIDGE_IP, MASK_SHORT, TAP_DEV}, 2 + constants::{BRIDGE_IP, MASK_SHORT}, 3 3 dnsmasq::setup_dnsmasq, 4 + types::VmOptions, 4 5 }; 5 6 use anyhow::{anyhow, Context, Result}; 6 7 use serde_json::Value; 7 8 8 9 use crate::command::run_command; 9 10 10 - fn check_tap_exists() -> bool { 11 - run_command("ip", &["link", "show", TAP_DEV], false) 11 + fn check_tap_exists(config: &VmOptions) -> bool { 12 + run_command("ip", &["link", "show", &config.tap], false) 12 13 .map(|output| output.status.success()) 13 14 .unwrap_or(false) 14 15 } 15 16 16 - fn check_bridge_exists() -> bool { 17 - run_command("ip", &["link", "show", BRIDGE_DEV], false) 17 + fn check_bridge_exists(config: &VmOptions) -> bool { 18 + run_command("ip", &["link", "show", &config.bridge], false) 18 19 .map(|output| output.status.success()) 19 20 .unwrap_or(false) 20 21 } 21 22 22 - pub fn setup_network() -> Result<()> { 23 - if check_tap_exists() { 24 - run_command("ip", &["addr", "flush", "dev", TAP_DEV], true)?; 23 + pub fn setup_network(config: &VmOptions) -> Result<()> { 24 + if check_tap_exists(config) { 25 + run_command("ip", &["addr", "flush", "dev", &config.tap], true)?; 25 26 } 26 27 27 - if check_tap_exists() && check_bridge_exists() { 28 + if check_tap_exists(config) && check_bridge_exists(config) { 28 29 println!("[✓] Network already configured. Skipping setup."); 29 30 return Ok(()); 30 31 } 31 32 32 - if !check_tap_exists() { 33 - println!("[+] Configuring {}...", TAP_DEV); 33 + if !check_tap_exists(config) { 34 + println!("[+] Configuring {}...", &config.tap); 34 35 run_command( 35 36 "ip", 36 - &["tuntap", "add", "dev", TAP_DEV, "mode", "tap"], 37 + &["tuntap", "add", "dev", &config.tap, "mode", "tap"], 37 38 true, 38 39 )?; 39 - run_command("ip", &["link", "set", "dev", TAP_DEV, "up"], true)?; 40 + run_command("ip", &["link", "set", "dev", &config.tap, "up"], true)?; 40 41 } 41 42 42 - if !check_bridge_exists() { 43 - println!("[+] Configuring {}...", BRIDGE_DEV); 43 + if !check_bridge_exists(config) { 44 + println!("[+] Configuring {}...", config.bridge); 44 45 run_command( 45 46 "ip", 46 - &["link", "add", "name", BRIDGE_DEV, "type", "bridge"], 47 + &["link", "add", "name", &config.bridge, "type", "bridge"], 47 48 true, 48 49 )?; 49 - run_command("ip", &["link", "set", BRIDGE_DEV, "up"], true)?; 50 - run_command("ip", &["link", "set", TAP_DEV, "master", BRIDGE_DEV], true)?; 50 + run_command("ip", &["link", "set", &config.bridge, "up"], true)?; 51 + run_command( 52 + "ip", 53 + &["link", "set", &config.tap, "master", &config.bridge], 54 + true, 55 + )?; 51 56 run_command( 52 57 "ip", 53 58 &[ ··· 55 60 "add", 56 61 &format!("{}{}", BRIDGE_IP, MASK_SHORT), 57 62 "dev", 58 - BRIDGE_DEV, 63 + &config.bridge, 59 64 ], 60 65 true, 61 66 )?; ··· 112 117 113 118 run_command("iptables", &["-P", "FORWARD", "ACCEPT"], true)?; 114 119 115 - setup_dnsmasq()?; 120 + setup_dnsmasq(config)?; 116 121 117 122 Ok(()) 118 123 }
+10
crates/firecracker-vm/src/types.rs
··· 1 1 use fire_config::FireConfig; 2 2 use firecracker_prepare::Distro; 3 3 4 + use crate::constants::{BRIDGE_DEV, FC_MAC, FIRECRACKER_SOCKET, TAP_DEV}; 5 + 4 6 #[derive(Default, Clone)] 5 7 pub struct VmOptions { 6 8 pub debian: Option<bool>, ··· 12 14 pub vmlinux: Option<String>, 13 15 pub rootfs: Option<String>, 14 16 pub bootargs: Option<String>, 17 + pub bridge: String, 18 + pub tap: String, 19 + pub api_socket: String, 20 + pub mac_address: String, 15 21 } 16 22 17 23 impl From<FireConfig> for VmOptions { ··· 27 33 vmlinux: vm.vmlinux, 28 34 rootfs: vm.rootfs, 29 35 bootargs: vm.boot_args, 36 + bridge: vm.bridge.unwrap_or(BRIDGE_DEV.into()), 37 + tap: vm.tap.unwrap_or(TAP_DEV.into()), 38 + api_socket: vm.api_socket.unwrap_or(FIRECRACKER_SOCKET.into()), 39 + mac_address: vm.mac.unwrap_or(FC_MAC.into()), 30 40 } 31 41 } 32 42 }