github.com/sneal/packer@v0.5.2/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 guiArgument := "sdl" 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 if config.Headless == true { 71 ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" + 72 "In headless mode, errors during the boot sequence or OS setup\n" + 73 "won't be easily visible. Use at your own discretion.") 74 guiArgument = "none" 75 } 76 77 defaultArgs := make(map[string]string) 78 defaultArgs["-name"] = vmName 79 defaultArgs["-machine"] = fmt.Sprintf("type=pc-1.0,accel=%s", config.Accelerator) 80 defaultArgs["-display"] = guiArgument 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 // Determine if we have a floppy disk to attach 91 if floppyPathRaw, ok := state.GetOk("floppy_path"); ok { 92 defaultArgs["-fda"] = floppyPathRaw.(string) 93 } else { 94 log.Println("Qemu Builder has no floppy files, not attaching a floppy.") 95 } 96 97 inArgs := make(map[string][]string) 98 if len(config.QemuArgs) > 0 { 99 ui.Say("Overriding defaults Qemu arguments with QemuArgs...") 100 101 httpPort := state.Get("http_port").(uint) 102 tplData := qemuArgsTemplateData{ 103 "10.0.2.2", 104 httpPort, 105 config.HTTPDir, 106 config.OutputDir, 107 config.VMName, 108 } 109 newQemuArgs, err := processArgs(config.QemuArgs, config.tpl, &tplData) 110 if err != nil { 111 return nil, err 112 } 113 114 // because qemu supports multiple appearances of the same 115 // switch, just different values, each key in the args hash 116 // will have an array of string values 117 for _, qemuArgs := range newQemuArgs { 118 key := qemuArgs[0] 119 val := strings.Join(qemuArgs[1:], "") 120 if _, ok := inArgs[key]; !ok { 121 inArgs[key] = make([]string, 0) 122 } 123 if len(val) > 0 { 124 inArgs[key] = append(inArgs[key], val) 125 } 126 } 127 } 128 129 // get any remaining missing default args from the default settings 130 for key := range defaultArgs { 131 if _, ok := inArgs[key]; !ok { 132 arg := make([]string, 1) 133 arg[0] = defaultArgs[key] 134 inArgs[key] = arg 135 } 136 } 137 138 // Flatten to array of strings 139 outArgs := make([]string, 0) 140 for key, values := range inArgs { 141 if len(values) > 0 { 142 for idx := range values { 143 outArgs = append(outArgs, key, values[idx]) 144 } 145 } else { 146 outArgs = append(outArgs, key) 147 } 148 } 149 150 return outArgs, nil 151 } 152 153 func processArgs(args [][]string, tpl *packer.ConfigTemplate, tplData *qemuArgsTemplateData) ([][]string, error) { 154 var err error 155 156 if args == nil { 157 return make([][]string, 0), err 158 } 159 160 newArgs := make([][]string, len(args)) 161 for argsIdx, rowArgs := range args { 162 parms := make([]string, len(rowArgs)) 163 newArgs[argsIdx] = parms 164 for i, parm := range rowArgs { 165 parms[i], err = tpl.Process(parm, &tplData) 166 if err != nil { 167 return nil, err 168 } 169 } 170 } 171 172 return newArgs, err 173 }