github.com/kimor79/packer@v0.8.7-0.20151221212622-d507b18eb4cf/builder/qemu/step_run.go (about)

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