d2vm/cmd/d2vm/run/qemu.go

124 lines
3.2 KiB
Go

package run
import (
"os"
"runtime"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"go.linka.cloud/d2vm/pkg/qemu"
)
const (
qemuNetworkingNone string = "none"
qemuNetworkingUser = "user"
qemuNetworkingTap = "tap"
qemuNetworkingBridge = "bridge"
qemuNetworkingDefault = qemuNetworkingUser
)
var (
defaultArch string
defaultAccel string
enableGUI bool
disks Disks
data string
accel string
arch string
cpus uint
mem uint
qemuCmd string
qemuDetached bool
networking string
publishFlags MultipleFlag
QemuCmd = &cobra.Command{
Use: "qemu [options] [image-path]",
Short: "Run the virtual machine image with qemu",
Args: cobra.ExactArgs(1),
Run: Qemu,
}
)
func init() {
switch runtime.GOARCH {
case "arm64":
defaultArch = "aarch64"
case "amd64":
defaultArch = "x86_64"
case "s390x":
defaultArch = "s390x"
}
switch {
case runtime.GOARCH == "s390x":
defaultAccel = "kvm"
case haveKVM():
defaultAccel = "kvm:tcg"
case runtime.GOOS == "darwin":
defaultAccel = "hvf:tcg"
}
flags := QemuCmd.Flags()
flags.BoolVar(&enableGUI, "gui", false, "Set qemu to use video output instead of stdio")
// Paths and settings for disks
flags.Var(&disks, "disk", "Disk config, may be repeated. [file=]path[,size=1G][,format=qcow2]")
// VM configuration
flags.StringVar(&accel, "accel", defaultAccel, "Choose acceleration mode. Use 'tcg' to disable it.")
flags.StringVar(&arch, "arch", defaultArch, "Type of architecture to use, e.g. x86_64, aarch64, s390x")
flags.UintVar(&cpus, "cpus", 1, "Number of CPUs")
flags.UintVar(&mem, "mem", 1024, "Amount of memory in MB")
// Backend configuration
flags.StringVar(&qemuCmd, "qemu", "", "Path to the qemu binary (otherwise look in $PATH)")
flags.BoolVar(&qemuDetached, "detached", false, "Set qemu container to run in the background")
// Networking
flags.StringVar(&networking, "networking", qemuNetworkingDefault, "Networking mode. Valid options are 'default', 'user', 'bridge[,name]', tap[,name] and 'none'. 'user' uses QEMUs userspace networking. 'bridge' connects to a preexisting bridge. 'tap' uses a prexisting tap device. 'none' disables networking.`")
flags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])")
}
func Qemu(cmd *cobra.Command, args []string) {
path := args[0]
if _, err := os.Stat(path); err != nil {
log.Fatal(err)
}
var publishedPorts []PublishedPort
for _, publish := range publishFlags {
p, err := NewPublishedPort(publish)
if err != nil {
log.Fatal(err)
}
publishedPorts = append(publishedPorts, p)
}
opts := []qemu.Option{
qemu.WithDisks(disks...),
qemu.WithAccel(accel),
qemu.WithArch(arch),
qemu.WithCPUs(cpus),
qemu.WithMemory(mem),
qemu.WithNetworking(networking),
qemu.WithStdin(os.Stdin),
qemu.WithStdout(os.Stdout),
qemu.WithStderr(os.Stderr),
}
if enableGUI {
opts = append(opts, qemu.WithGUI())
}
if qemuDetached {
opts = append(opts, qemu.WithDetached())
}
if err := qemu.Run(cmd.Context(), path, opts...); err != nil {
log.Fatal(err)
}
}
func haveKVM() bool {
_, err := os.Stat("/dev/kvm")
return !os.IsNotExist(err)
}