Added template processing for QemuArgs, floppy_files for auto-building floppies which attach to the VM at startup, and run_once flag to trigger just one powerup/powerdown.
This commit is contained in:
parent
32b480a263
commit
08f5131746
|
@ -58,6 +58,7 @@ type config struct {
|
|||
BootCommand []string `mapstructure:"boot_command"`
|
||||
DiskInterface string `mapstructure:"disk_interface"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||
Format string `mapstructure:"format"`
|
||||
Headless bool `mapstructure:"headless"`
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
|
@ -79,6 +80,7 @@ type config struct {
|
|||
VNCPortMin uint `mapstructure:"vnc_port_min"`
|
||||
VNCPortMax uint `mapstructure:"vnc_port_max"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
RunOnce bool `mapstructure:"run_once"`
|
||||
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
RawSingleISOUrl string `mapstructure:"iso_url"`
|
||||
|
@ -150,8 +152,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
b.config.VNCPortMax = 6000
|
||||
}
|
||||
|
||||
if b.config.QemuArgs == nil {
|
||||
b.config.QemuArgs = make([][]string, 0)
|
||||
for i, args := range b.config.QemuArgs {
|
||||
for j, arg := range args {
|
||||
if err := b.config.tpl.Validate(arg); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Error processing qemu-system_x86-64[%d][%d]: %s", i, j, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.VMName == "" {
|
||||
|
@ -162,6 +169,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
b.config.Format = "qcow2"
|
||||
}
|
||||
|
||||
if b.config.FloppyFiles == nil {
|
||||
b.config.FloppyFiles = make([]string, 0)
|
||||
}
|
||||
|
||||
if b.config.NetDevice == "" {
|
||||
b.config.NetDevice = "virtio-net"
|
||||
}
|
||||
|
@ -215,6 +226,16 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
for i, file := range b.config.FloppyFiles {
|
||||
var err error
|
||||
b.config.FloppyFiles[i], err = b.config.tpl.Process(file, nil)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Error processing floppy_files[%d]: %s",
|
||||
i, err))
|
||||
}
|
||||
}
|
||||
|
||||
if !(b.config.Format == "qcow2" || b.config.Format == "raw") {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("invalid format, only 'qcow2' or 'img' are allowed"))
|
||||
|
@ -336,13 +357,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
||||
}
|
||||
|
||||
for i, args := range b.config.QemuArgs {
|
||||
for j, arg := range args {
|
||||
if err := b.config.tpl.Validate(arg); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Error processing qemu-system_x86-64[%d][%d]: %s", i, j, err))
|
||||
}
|
||||
}
|
||||
if b.config.QemuArgs == nil {
|
||||
b.config.QemuArgs = make([][]string, 0)
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
|
@ -368,6 +384,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Url: b.config.ISOUrls,
|
||||
},
|
||||
new(stepPrepareOutputDir),
|
||||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyFiles,
|
||||
},
|
||||
new(stepCreateDisk),
|
||||
new(stepHTTPServer),
|
||||
new(stepForwardSSH),
|
||||
|
@ -376,15 +395,23 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
BootDrive: "d",
|
||||
Message: "Starting VM, booting from CD-ROM",
|
||||
},
|
||||
&stepBootWait{},
|
||||
&stepTypeBootCommand{},
|
||||
&stepWaitForShutdown{
|
||||
Message: "Waiting for initial VM boot to shut down",
|
||||
},
|
||||
&stepRun{
|
||||
BootDrive: "c",
|
||||
Message: "Starting VM, booting from hard disk",
|
||||
},
|
||||
}
|
||||
|
||||
if !b.config.RunOnce {
|
||||
steps = append(steps,
|
||||
&stepBootWait{},
|
||||
&stepTypeBootCommand{},
|
||||
&stepWaitForShutdown{
|
||||
Message: "Waiting for initial VM boot to shut down",
|
||||
},
|
||||
&stepRun{
|
||||
BootDrive: "c",
|
||||
Message: "Starting VM, booting from hard disk",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
&common.StepConnectSSH{
|
||||
SSHAddress: sshAddress,
|
||||
SSHConfig: sshConfig,
|
||||
|
@ -392,7 +419,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
},
|
||||
new(common.StepProvision),
|
||||
new(stepShutdown),
|
||||
}
|
||||
)
|
||||
|
||||
// Setup the state bag
|
||||
state := new(multistep.BasicStateBag)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
@ -14,13 +15,27 @@ type stepRun struct {
|
|||
Message string
|
||||
}
|
||||
|
||||
type qemuArgsTemplateData struct {
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
HTTPDir string
|
||||
OutputDir string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say(s.Message)
|
||||
|
||||
command := getCommandArgs(s.BootDrive, state)
|
||||
command, err := getCommandArgs(s.BootDrive, state)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error processing QemuArggs: %s", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err := driver.Qemu(command...); err != nil {
|
||||
err := fmt.Errorf("Error launching VM: %s", err)
|
||||
ui.Error(err.Error())
|
||||
|
@ -39,7 +54,7 @@ func (s *stepRun) Cleanup(state multistep.StateBag) {
|
|||
}
|
||||
}
|
||||
|
||||
func getCommandArgs(bootDrive string, state multistep.StateBag) []string {
|
||||
func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) {
|
||||
config := state.Get("config").(*config)
|
||||
isoPath := state.Get("iso_path").(string)
|
||||
vncPort := state.Get("vnc_port").(uint)
|
||||
|
@ -72,14 +87,34 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) []string {
|
|||
defaultArgs["-redir"] = fmt.Sprintf("tcp:%v::22", sshHostPort)
|
||||
defaultArgs["-vnc"] = vnc
|
||||
|
||||
// Determine if we have a floppy disk to attach
|
||||
if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
|
||||
defaultArgs["-fda"] = floppyPathRaw.(string)
|
||||
} else {
|
||||
log.Println("Qemu Builder has no floppy files, not attaching a floppy.")
|
||||
}
|
||||
|
||||
inArgs := make(map[string][]string)
|
||||
if len(config.QemuArgs) > 0 {
|
||||
ui.Say("Overriding defaults Qemu arguments with QemuArgs...")
|
||||
|
||||
// becuase qemu supports multiple appearances of the same
|
||||
httpPort := state.Get("http_port").(uint)
|
||||
tplData := qemuArgsTemplateData{
|
||||
"10.0.2.2",
|
||||
httpPort,
|
||||
config.HTTPDir,
|
||||
config.OutputDir,
|
||||
config.VMName,
|
||||
}
|
||||
newQemuArgs, err := processArgs(config.QemuArgs, config.tpl, &tplData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// because qemu supports multiple appearances of the same
|
||||
// switch, just different values, each key in the args hash
|
||||
// will have an array of string values
|
||||
for _, qemuArgs := range config.QemuArgs {
|
||||
for _, qemuArgs := range newQemuArgs {
|
||||
key := qemuArgs[0]
|
||||
val := strings.Join(qemuArgs[1:], "")
|
||||
if _, ok := inArgs[key]; !ok {
|
||||
|
@ -112,5 +147,27 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) []string {
|
|||
}
|
||||
}
|
||||
|
||||
return outArgs
|
||||
return outArgs, nil
|
||||
}
|
||||
|
||||
func processArgs(args [][]string, tpl *packer.ConfigTemplate, tplData *qemuArgsTemplateData) ([][]string, error) {
|
||||
var err error
|
||||
|
||||
if args == nil {
|
||||
return make([][]string, 0), err
|
||||
}
|
||||
|
||||
newArgs := make([][]string, len(args))
|
||||
for argsIdx, rowArgs := range args {
|
||||
parms := make([]string, len(rowArgs))
|
||||
newArgs[argsIdx] = parms
|
||||
for i, parm := range rowArgs {
|
||||
parms[i], err = tpl.Process(parm, &tplData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newArgs, err
|
||||
}
|
||||
|
|
|
@ -118,6 +118,14 @@ Optional:
|
|||
* `format` (string) - Either "qcow2" or "img", this specifies the output
|
||||
format of the virtual machine image. This defaults to "qcow2".
|
||||
|
||||
* `floppy_files` (array of strings) - A list of files to place onto a floppy
|
||||
disk that gets attached when Packer powers up the VM. This is most useful
|
||||
for unattended Windows installs, which look for an `Autounattend.xml` file
|
||||
on removable media. By default no floppy will be attached. All files
|
||||
listed in this setting get placed into the root directory of the floppy
|
||||
and teh floppy is attached as the first floppy device. Currently, no
|
||||
support exists for sub-directories.
|
||||
|
||||
* `headless` (bool) - Packer defaults to building virtual machines by
|
||||
launching a GUI that shows the console of the machine being built.
|
||||
When this value is set to true, the machine will start without a console.
|
||||
|
@ -191,6 +199,16 @@ Optional:
|
|||
By default this is "output-BUILDNAME" where "BUILDNAME" is the name
|
||||
of the build.
|
||||
|
||||
* `run_once` (boolean) - When set to true, run_once causes the builder to run
|
||||
Qemu only once, rather than twice. Normally (default false) the builder
|
||||
will run Qemu once for an initial OS install, then switch the CDROM device
|
||||
and boot device for a second run. In that case, Packer does not wait for
|
||||
SSH connections until the second power up of the VM. This approach is often
|
||||
necessary in Linux distribution installs. However, in many Windows unattended
|
||||
installs, the setup handles reboots and dealing with the CDROM as the boot
|
||||
device. With care, a power-down/power-up setting (run_once is set to false)
|
||||
could work if the unattended install is set to restart into audit mode.
|
||||
|
||||
* `shutdown_command` (string) - The command to use to gracefully shut down
|
||||
the machine once all the provisioning is done. By default this is an empty
|
||||
string, which tells Packer to just forcefully shut down the machine.
|
||||
|
|
Loading…
Reference in New Issue