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