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