github.com/kimor79/packer@v0.8.7-0.20151221212622-d507b18eb4cf/builder/qemu/step_run.go (about) 1 package qemu 2 3 import ( 4 "fmt" 5 "log" 6 "path/filepath" 7 "strconv" 8 "strings" 9 10 "github.com/mitchellh/multistep" 11 "github.com/mitchellh/packer/packer" 12 "github.com/mitchellh/packer/template/interpolate" 13 ) 14 15 // stepRun runs the virtual machine 16 type stepRun struct { 17 BootDrive string 18 Message string 19 } 20 21 type qemuArgsTemplateData struct { 22 HTTPIP string 23 HTTPPort uint 24 HTTPDir string 25 OutputDir string 26 Name string 27 } 28 29 func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction { 30 driver := state.Get("driver").(Driver) 31 ui := state.Get("ui").(packer.Ui) 32 33 ui.Say(s.Message) 34 35 command, err := getCommandArgs(s.BootDrive, state) 36 if err != nil { 37 err := fmt.Errorf("Error processing QemuArggs: %s", err) 38 ui.Error(err.Error()) 39 return multistep.ActionHalt 40 } 41 42 if err := driver.Qemu(command...); err != nil { 43 err := fmt.Errorf("Error launching VM: %s", err) 44 ui.Error(err.Error()) 45 return multistep.ActionHalt 46 } 47 48 return multistep.ActionContinue 49 } 50 51 func (s *stepRun) Cleanup(state multistep.StateBag) { 52 driver := state.Get("driver").(Driver) 53 ui := state.Get("ui").(packer.Ui) 54 55 if err := driver.Stop(); err != nil { 56 ui.Error(fmt.Sprintf("Error shutting down VM: %s", err)) 57 } 58 } 59 60 func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) { 61 config := state.Get("config").(*Config) 62 isoPath := state.Get("iso_path").(string) 63 vncPort := state.Get("vnc_port").(uint) 64 sshHostPort := state.Get("sshHostPort").(uint) 65 ui := state.Get("ui").(packer.Ui) 66 driver := state.Get("driver").(Driver) 67 68 vnc := fmt.Sprintf("0.0.0.0:%d", vncPort-5900) 69 vmName := config.VMName 70 imgPath := filepath.Join(config.OutputDir, vmName) 71 72 defaultArgs := make(map[string]interface{}) 73 var deviceArgs []string 74 var driveArgs []string 75 76 defaultArgs["-name"] = vmName 77 defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType) 78 defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", sshHostPort, config.Comm.Port()) 79 80 qemuVersion, err := driver.Version() 81 if err != nil { 82 return nil, err 83 } 84 parts := strings.Split(qemuVersion, ".") 85 qemuMajor, err := strconv.Atoi(parts[0]) 86 if err != nil { 87 return nil, err 88 } 89 if qemuMajor >= 2 { 90 if config.DiskInterface == "virtio-scsi" { 91 deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0", "scsi-hd,bus=scsi0.0,drive=drive0") 92 driveArgs = append(driveArgs, fmt.Sprintf("if=none,file=%s,id=drive0,cache=%s,discard=%s", imgPath, config.DiskCache, config.DiskDiscard)) 93 } else { 94 driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard)) 95 } 96 } else { 97 defaultArgs["-drive"] = fmt.Sprintf("file=%s,if=%s,cache=%s", imgPath, config.DiskInterface, config.DiskCache) 98 } 99 deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice)) 100 101 if config.Headless == true { 102 ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" + 103 "In headless mode, errors during the boot sequence or OS setup\n" + 104 "won't be easily visible. Use at your own discretion.") 105 } else { 106 if qemuMajor >= 2 { 107 defaultArgs["-display"] = "sdl" 108 } else { 109 ui.Message("WARNING: The version of qemu on your host doesn't support display mode.\n" + 110 "The display parameter will be ignored.") 111 } 112 } 113 114 defaultArgs["-device"] = deviceArgs 115 defaultArgs["-drive"] = driveArgs 116 117 if !config.DiskImage { 118 defaultArgs["-cdrom"] = isoPath 119 } 120 defaultArgs["-boot"] = bootDrive 121 defaultArgs["-m"] = "512M" 122 defaultArgs["-vnc"] = vnc 123 124 // Append the accelerator to the machine type if it is specified 125 if config.Accelerator != "none" { 126 defaultArgs["-machine"] = fmt.Sprintf("%s,accel=%s", defaultArgs["-machine"], config.Accelerator) 127 } else { 128 ui.Message("WARNING: The VM will be started with no hardware acceleration.\n" + 129 "The installation may take considerably longer to finish.\n") 130 } 131 132 // Determine if we have a floppy disk to attach 133 if floppyPathRaw, ok := state.GetOk("floppy_path"); ok { 134 defaultArgs["-fda"] = floppyPathRaw.(string) 135 } else { 136 log.Println("Qemu Builder has no floppy files, not attaching a floppy.") 137 } 138 139 inArgs := make(map[string][]string) 140 if len(config.QemuArgs) > 0 { 141 ui.Say("Overriding defaults Qemu arguments with QemuArgs...") 142 143 httpPort := state.Get("http_port").(uint) 144 ctx := config.ctx 145 ctx.Data = qemuArgsTemplateData{ 146 "10.0.2.2", 147 httpPort, 148 config.HTTPDir, 149 config.OutputDir, 150 config.VMName, 151 } 152 newQemuArgs, err := processArgs(config.QemuArgs, &ctx) 153 if err != nil { 154 return nil, err 155 } 156 157 // because qemu supports multiple appearances of the same 158 // switch, just different values, each key in the args hash 159 // will have an array of string values 160 for _, qemuArgs := range newQemuArgs { 161 key := qemuArgs[0] 162 val := strings.Join(qemuArgs[1:], "") 163 if _, ok := inArgs[key]; !ok { 164 inArgs[key] = make([]string, 0) 165 } 166 if len(val) > 0 { 167 inArgs[key] = append(inArgs[key], val) 168 } 169 } 170 } 171 172 // get any remaining missing default args from the default settings 173 for key := range defaultArgs { 174 if _, ok := inArgs[key]; !ok { 175 arg := make([]string, 1) 176 switch defaultArgs[key].(type) { 177 case string: 178 arg[0] = defaultArgs[key].(string) 179 case []string: 180 arg = defaultArgs[key].([]string) 181 } 182 inArgs[key] = arg 183 } 184 } 185 186 // Flatten to array of strings 187 outArgs := make([]string, 0) 188 for key, values := range inArgs { 189 if len(values) > 0 { 190 for idx := range values { 191 outArgs = append(outArgs, key, values[idx]) 192 } 193 } else { 194 outArgs = append(outArgs, key) 195 } 196 } 197 198 return outArgs, nil 199 } 200 201 func processArgs(args [][]string, ctx *interpolate.Context) ([][]string, error) { 202 var err error 203 204 if args == nil { 205 return make([][]string, 0), err 206 } 207 208 newArgs := make([][]string, len(args)) 209 for argsIdx, rowArgs := range args { 210 parms := make([]string, len(rowArgs)) 211 newArgs[argsIdx] = parms 212 for i, parm := range rowArgs { 213 parms[i], err = interpolate.Render(parm, ctx) 214 if err != nil { 215 return nil, err 216 } 217 } 218 } 219 220 return newArgs, err 221 }