diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 4ac22b59b..016515617 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -54,9 +54,10 @@ var netDevice = map[string]bool{ } var diskInterface = map[string]bool{ - "ide": true, - "scsi": true, - "virtio": true, + "ide": true, + "scsi": true, + "virtio": true, + "virtio-scsi": true, } var diskCache = map[string]bool{ diff --git a/builder/qemu/driver.go b/builder/qemu/driver.go index 5c402cecb..77c68737d 100644 --- a/builder/qemu/driver.go +++ b/builder/qemu/driver.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "fmt" - "github.com/mitchellh/multistep" "io" "log" "os/exec" @@ -14,6 +13,8 @@ import ( "syscall" "time" "unicode" + + "github.com/mitchellh/multistep" ) type DriverCancelCallback func(state multistep.StateBag) bool @@ -188,8 +189,8 @@ func (d *QemuDriver) Version() (string, error) { versionOutput := strings.TrimSpace(stdout.String()) log.Printf("Qemu --version output: %s", versionOutput) - versionRe := regexp.MustCompile("qemu-kvm-[0-9]\\.[0-9]") - matches := versionRe.Split(versionOutput, 2) + versionRe := regexp.MustCompile("[\\.[0-9]+]*") + matches := versionRe.FindStringSubmatch(versionOutput) if len(matches) == 0 { return "", fmt.Errorf("No version found: %s", versionOutput) } diff --git a/builder/qemu/step_run.go b/builder/qemu/step_run.go index 356481085..74db88a15 100644 --- a/builder/qemu/step_run.go +++ b/builder/qemu/step_run.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "path/filepath" + "strconv" "strings" "github.com/mitchellh/multistep" @@ -62,27 +63,57 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error vncPort := state.Get("vnc_port").(uint) sshHostPort := state.Get("sshHostPort").(uint) ui := state.Get("ui").(packer.Ui) + driver := state.Get("driver").(Driver) vnc := fmt.Sprintf("0.0.0.0:%d", vncPort-5900) vmName := config.VMName imgPath := filepath.Join(config.OutputDir, vmName) - defaultArgs := make(map[string]string) + defaultArgs := make(map[string]interface{}) + var deviceArgs []string + var driveArgs []string + + defaultArgs["-name"] = vmName + defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType) + defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", sshHostPort, config.Comm.Port()) + + qemuVersion, err := driver.Version() + if err != nil { + return nil, err + } + parts := strings.Split(qemuVersion, ".") + qemuMajor, err := strconv.Atoi(parts[0]) + if err != nil { + return nil, err + } + if qemuMajor >= 2 { + if config.DiskInterface == "virtio-scsi" { + deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0", "scsi-hd,bus=scsi0.0,drive=drive0") + driveArgs = append(driveArgs, fmt.Sprintf("if=none,file=%s,id=drive0,cache=%s,discard=%s", imgPath, config.DiskCache, config.DiskDiscard)) + } else { + driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard)) + } + } else { + defaultArgs["-drive"] = fmt.Sprintf("file=%s,if=%s,cache=%s", imgPath, config.DiskInterface, config.DiskCache) + } + deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice)) if config.Headless == true { ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" + "In headless mode, errors during the boot sequence or OS setup\n" + "won't be easily visible. Use at your own discretion.") } else { - defaultArgs["-display"] = "sdl" + if qemuMajor >= 2 { + defaultArgs["-display"] = "sdl" + } else { + ui.Message("WARNING: The version of qemu on your host doesn't support display mode.\n" + + "The display parameter will be ignored.") + } } - defaultArgs["-name"] = vmName - defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType) - defaultArgs["-netdev"] = fmt.Sprintf( - "user,id=user.0,hostfwd=tcp::%v-:%d", sshHostPort, config.Comm.Port()) - defaultArgs["-device"] = fmt.Sprintf("%s,netdev=user.0", config.NetDevice) - defaultArgs["-drive"] = fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard) + defaultArgs["-device"] = deviceArgs + defaultArgs["-drive"] = driveArgs + if !config.DiskImage { defaultArgs["-cdrom"] = isoPath } @@ -92,7 +123,7 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error // Append the accelerator to the machine type if it is specified if config.Accelerator != "none" { - defaultArgs["-machine"] += fmt.Sprintf(",accel=%s", config.Accelerator) + defaultArgs["-machine"] = fmt.Sprintf("%s,accel=%s", defaultArgs["-machine"], config.Accelerator) } else { ui.Message("WARNING: The VM will be started with no hardware acceleration.\n" + "The installation may take considerably longer to finish.\n") @@ -142,7 +173,12 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error for key := range defaultArgs { if _, ok := inArgs[key]; !ok { arg := make([]string, 1) - arg[0] = defaultArgs[key] + switch defaultArgs[key].(type) { + case string: + arg[0] = defaultArgs[key].(string) + case []string: + arg = defaultArgs[key].([]string) + } inArgs[key] = arg } }