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  }