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  }