A simple, powerful CLI tool to spin up OpenIndiana virtual machines with QEMU

Add support for network bridge configuration in VM setup; include bridge option in command and utility functions

Changed files
+119 -2
+9
main.ts
··· 1 1 #!/usr/bin/env -S deno run --allow-run --allow-read --allow-env 2 2 3 3 import { Command } from "@cliffy/command"; 4 + import { createBridgeNetworkIfNeeded } from "./network.ts"; 4 5 import { 5 6 createDriveImageIfNeeded, 6 7 downloadIso, ··· 43 44 default: "20G", 44 45 }, 45 46 ) 47 + .option( 48 + "-b, --bridge <name:string>", 49 + "Name of the network bridge to use for networking (e.g., br0)", 50 + ) 46 51 .example( 47 52 "Default usage", 48 53 "openindiana-up", ··· 76 81 77 82 if (!input && options.drive && !await emptyDiskImage(options.drive)) { 78 83 isoPath = null; 84 + } 85 + 86 + if (options.bridge) { 87 + await createBridgeNetworkIfNeeded(options.bridge); 79 88 } 80 89 81 90 await runQemu(isoPath, options);
+104
network.ts
··· 1 + import chalk from "chalk"; 2 + 3 + export async function setupQemuBridge(bridgeName: string): Promise<void> { 4 + const bridgeConfPath = "/etc/qemu/bridge.conf"; 5 + const bridgeConfContent = await Deno.readTextFile(bridgeConfPath).catch( 6 + () => "", 7 + ); 8 + if (bridgeConfContent.includes(`allow ${bridgeName}`)) { 9 + console.log( 10 + chalk.greenBright( 11 + `QEMU bridge configuration for ${bridgeName} already exists.`, 12 + ), 13 + ); 14 + return; 15 + } 16 + 17 + console.log( 18 + chalk.blueBright( 19 + `Adding QEMU bridge configuration for ${bridgeName}...`, 20 + ), 21 + ); 22 + 23 + const cmd = new Deno.Command("sudo", { 24 + args: [ 25 + "sh", 26 + "-c", 27 + `mkdir -p /etc/qemu && echo "allow ${bridgeName}" >> ${bridgeConfPath}`, 28 + ], 29 + stdin: "inherit", 30 + stdout: "inherit", 31 + stderr: "inherit", 32 + }); 33 + const status = await cmd.spawn().status; 34 + 35 + if (!status.success) { 36 + console.error( 37 + chalk.redBright( 38 + `Failed to add QEMU bridge configuration for ${bridgeName}.`, 39 + ), 40 + ); 41 + Deno.exit(status.code); 42 + } 43 + 44 + console.log( 45 + chalk.greenBright( 46 + `QEMU bridge configuration for ${bridgeName} added successfully.`, 47 + ), 48 + ); 49 + } 50 + 51 + export async function createBridgeNetworkIfNeeded( 52 + bridgeName: string, 53 + ): Promise<void> { 54 + const bridgeExistsCmd = new Deno.Command("ip", { 55 + args: ["link", "show", bridgeName], 56 + stdout: "null", 57 + stderr: "null", 58 + }); 59 + 60 + const bridgeExistsStatus = await bridgeExistsCmd.spawn().status; 61 + if (bridgeExistsStatus.success) { 62 + console.log( 63 + chalk.greenBright(`Network bridge ${bridgeName} already exists.`), 64 + ); 65 + await setupQemuBridge(bridgeName); 66 + return; 67 + } 68 + 69 + console.log(chalk.blueBright(`Creating network bridge ${bridgeName}...`)); 70 + const createBridgeCmd = new Deno.Command("sudo", { 71 + args: ["ip", "link", "add", bridgeName, "type", "bridge"], 72 + stdin: "inherit", 73 + stdout: "inherit", 74 + stderr: "inherit", 75 + }); 76 + 77 + let status = await createBridgeCmd.spawn().status; 78 + if (!status.success) { 79 + console.error( 80 + chalk.redBright(`Failed to create network bridge ${bridgeName}.`), 81 + ); 82 + Deno.exit(status.code); 83 + } 84 + 85 + const bringUpBridgeCmd = new Deno.Command("sudo", { 86 + args: ["ip", "link", "set", "dev", bridgeName, "up"], 87 + stdin: "inherit", 88 + stdout: "inherit", 89 + stderr: "inherit", 90 + }); 91 + status = await bringUpBridgeCmd.spawn().status; 92 + if (!status.success) { 93 + console.error( 94 + chalk.redBright(`Failed to bring up network bridge ${bridgeName}.`), 95 + ); 96 + Deno.exit(status.code); 97 + } 98 + 99 + console.log( 100 + chalk.greenBright(`Network bridge ${bridgeName} created and up.`), 101 + ); 102 + 103 + await setupQemuBridge(bridgeName); 104 + }
+6 -2
utils.ts
··· 11 11 drive?: string; 12 12 diskFormat: string; 13 13 size: string; 14 + bridge?: string; 14 15 } 15 16 16 17 async function du(path: string): Promise<number> { ··· 88 89 isoPath: string | null, 89 90 options: Options, 90 91 ): Promise<void> { 91 - const cmd = new Deno.Command("qemu-system-x86_64", { 92 + const cmd = new Deno.Command(options.bridge ? "sudo" : "qemu-system-x86_64", { 92 93 args: [ 94 + ..._.compact([options.bridge && "qemu-system-x86_64"]), 93 95 "-enable-kvm", 94 96 "-cpu", 95 97 options.cpu, ··· 99 101 options.cpus.toString(), 100 102 ..._.compact([isoPath && "-cdrom", isoPath]), 101 103 "-netdev", 102 - "user,id=net0,hostfwd=tcp::2222-:22", 104 + options.bridge 105 + ? `bridge,id=net0,br=${options.bridge}` 106 + : "user,id=net0,hostfwd=tcp::2222-:22", 103 107 "-device", 104 108 "e1000,netdev=net0", 105 109 "-nographic",