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