Merge pull request #7791 from 70k10/qemuadddisks

Add additional disk support for QEMU builder.
pull/7800/head
Megan Marsh 7 years ago committed by GitHub
commit b565075813
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -96,34 +96,35 @@ type Config struct {
Comm communicator.Config `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
ISOSkipCache bool `mapstructure:"iso_skip_cache"`
Accelerator string `mapstructure:"accelerator"`
CpuCount int `mapstructure:"cpus"`
DiskInterface string `mapstructure:"disk_interface"`
DiskSize uint `mapstructure:"disk_size"`
DiskCache string `mapstructure:"disk_cache"`
DiskDiscard string `mapstructure:"disk_discard"`
DetectZeroes string `mapstructure:"disk_detect_zeroes"`
SkipCompaction bool `mapstructure:"skip_compaction"`
DiskCompression bool `mapstructure:"disk_compression"`
Format string `mapstructure:"format"`
Headless bool `mapstructure:"headless"`
DiskImage bool `mapstructure:"disk_image"`
UseBackingFile bool `mapstructure:"use_backing_file"`
MachineType string `mapstructure:"machine_type"`
MemorySize int `mapstructure:"memory"`
NetDevice string `mapstructure:"net_device"`
OutputDir string `mapstructure:"output_directory"`
QemuArgs [][]string `mapstructure:"qemuargs"`
QemuBinary string `mapstructure:"qemu_binary"`
ShutdownCommand string `mapstructure:"shutdown_command"`
SSHHostPortMin int `mapstructure:"ssh_host_port_min"`
SSHHostPortMax int `mapstructure:"ssh_host_port_max"`
UseDefaultDisplay bool `mapstructure:"use_default_display"`
VNCBindAddress string `mapstructure:"vnc_bind_address"`
VNCPortMin int `mapstructure:"vnc_port_min"`
VNCPortMax int `mapstructure:"vnc_port_max"`
VMName string `mapstructure:"vm_name"`
ISOSkipCache bool `mapstructure:"iso_skip_cache"`
Accelerator string `mapstructure:"accelerator"`
CpuCount int `mapstructure:"cpus"`
AdditionalDiskSize []string `mapstructure:"disk_additional_size"`
DiskInterface string `mapstructure:"disk_interface"`
DiskSize uint `mapstructure:"disk_size"`
DiskCache string `mapstructure:"disk_cache"`
DiskDiscard string `mapstructure:"disk_discard"`
DetectZeroes string `mapstructure:"disk_detect_zeroes"`
SkipCompaction bool `mapstructure:"skip_compaction"`
DiskCompression bool `mapstructure:"disk_compression"`
Format string `mapstructure:"format"`
Headless bool `mapstructure:"headless"`
DiskImage bool `mapstructure:"disk_image"`
UseBackingFile bool `mapstructure:"use_backing_file"`
MachineType string `mapstructure:"machine_type"`
MemorySize int `mapstructure:"memory"`
NetDevice string `mapstructure:"net_device"`
OutputDir string `mapstructure:"output_directory"`
QemuArgs [][]string `mapstructure:"qemuargs"`
QemuBinary string `mapstructure:"qemu_binary"`
ShutdownCommand string `mapstructure:"shutdown_command"`
SSHHostPortMin int `mapstructure:"ssh_host_port_min"`
SSHHostPortMax int `mapstructure:"ssh_host_port_max"`
UseDefaultDisplay bool `mapstructure:"use_default_display"`
VNCBindAddress string `mapstructure:"vnc_bind_address"`
VNCPortMin int `mapstructure:"vnc_port_min"`
VNCPortMax int `mapstructure:"vnc_port_max"`
VMName string `mapstructure:"vm_name"`
// These are deprecated, but we keep them around for BC
// TODO(@mitchellh): remove
@ -286,6 +287,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs, errors.New("use_backing_file can only be enabled for QCOW2 images and when disk_image is true"))
}
if b.config.DiskImage && len(b.config.AdditionalDiskSize) > 0 {
errs = packer.MultiErrorAppend(
errs, errors.New("disk_additional_size can only be used when disk_image is false"))
}
if _, ok := accels[b.config.Accelerator]; !ok {
errs = packer.MultiErrorAppend(
errs, errors.New("invalid accelerator, only 'kvm', 'tcg', 'xen', 'hax', 'hvf', 'whpx', or 'none' are allowed"))
@ -500,7 +506,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
state: make(map[string]interface{}),
}
artifact.state["diskName"] = state.Get("disk_filename").(string)
artifact.state["diskName"] = b.config.VMName
diskpaths, ok := state.Get("qemu_disk_paths").([]string)
if ok {
artifact.state["diskPaths"] = diskpaths
}
artifact.state["diskType"] = b.config.Format
artifact.state["diskSize"] = uint64(b.config.DiskSize)
artifact.state["domainType"] = b.config.Accelerator

@ -187,6 +187,36 @@ func TestBuilderPrepare_DiskSize(t *testing.T) {
}
}
func TestBuilderPrepare_AdditionalDiskSize(t *testing.T) {
var b Builder
config := testConfig()
config["disk_additional_size"] = []string{"1M"}
config["disk_image"] = true
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatalf("should have error")
}
delete(config, "disk_image")
config["disk_additional_size"] = []string{"1M"}
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.AdditionalDiskSize[0] != "1M" {
t.Fatalf("bad size: %s", b.config.AdditionalDiskSize)
}
}
func TestBuilderPrepare_Format(t *testing.T) {
var b Builder
config := testConfig()

@ -22,7 +22,7 @@ type stepConvertDisk struct{}
func (s *stepConvertDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
driver := state.Get("driver").(Driver)
diskName := state.Get("disk_filename").(string)
diskName := config.VMName
ui := state.Get("ui").(packer.Ui)
if config.SkipCompaction && !config.DiskCompression {

@ -19,7 +19,6 @@ func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multis
isoPath := state.Get("iso_path").(string)
ui := state.Get("ui").(packer.Ui)
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s", config.VMName))
name := config.VMName
command := []string{
"convert",
@ -40,8 +39,6 @@ func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multis
return multistep.ActionHalt
}
state.Put("disk_filename", name)
return multistep.ActionContinue
}

@ -3,6 +3,7 @@ package qemu
import (
"context"
"fmt"
"log"
"path/filepath"
"github.com/hashicorp/packer/helper/multistep"
@ -18,36 +19,54 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
name := config.VMName
path := filepath.Join(config.OutputDir, name)
command := []string{
"create",
"-f", config.Format,
if config.DiskImage && !config.UseBackingFile {
return multistep.ActionContinue
}
if config.UseBackingFile {
isoPath := state.Get("iso_path").(string)
command = append(command, "-b", isoPath)
var diskFullPaths, diskSizes []string
ui.Say("Creating required virtual machine disks")
// The 'main' or 'default' disk
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, name))
diskSizes = append(diskSizes, fmt.Sprintf("%dM", uint64(config.DiskSize)))
// Additional disks
if len(config.AdditionalDiskSize) > 0 {
for i, diskSize := range config.AdditionalDiskSize {
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s-%d", name, i+1))
diskFullPaths = append(diskFullPaths, path)
size := fmt.Sprintf("%s", diskSize)
diskSizes = append(diskSizes, size)
}
}
command = append(command,
path,
fmt.Sprintf("%vM", config.DiskSize),
)
// Create all required disks
for i, diskFullPath := range diskFullPaths {
log.Printf("[INFO] Creating disk with Path: %s and Size: %s", diskFullPath, diskSizes[i])
command := []string{
"create",
"-f", config.Format,
}
if config.DiskImage && !config.UseBackingFile {
return multistep.ActionContinue
}
if config.UseBackingFile && i == 0 {
isoPath := state.Get("iso_path").(string)
command = append(command, "-b", isoPath)
}
command = append(command,
diskFullPath,
diskSizes[i])
ui.Say("Creating hard drive...")
if err := driver.QemuImg(command...); err != nil {
err := fmt.Errorf("Error creating hard drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
if err := driver.QemuImg(command...); err != nil {
err := fmt.Errorf("Error creating hard drive: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
state.Put("disk_filename", name)
// Stash the disk paths so we can retrieve later
state.Put("qemu_disk_paths", diskFullPaths)
return multistep.ActionContinue
}

@ -94,19 +94,42 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
if qemuVersion.GreaterThanOrEqual(v2) {
if config.DiskInterface == "virtio-scsi" {
deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0", "scsi-hd,bus=scsi0.0,drive=drive0")
driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive0,cache=%s,discard=%s,format=%s", imgPath, config.DiskCache, config.DiskDiscard, config.Format)
if config.DetectZeroes != "off" {
driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
if config.DiskImage {
deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0", "scsi-hd,bus=scsi0.0,drive=drive0")
driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive0,cache=%s,discard=%s,format=%s", imgPath, config.DiskCache, config.DiskDiscard, config.Format)
if config.DetectZeroes != "off" {
driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
}
driveArgs = append(driveArgs, driveArgumentString)
} else {
deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0")
diskFullPaths := state.Get("qemu_disk_paths").([]string)
for i, diskFullPath := range diskFullPaths {
deviceArgs = append(deviceArgs, fmt.Sprintf("scsi-hd,bus=scsi0.0,drive=drive%d", i))
driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive%d,cache=%s,discard=%s,format=%s", diskFullPath, i, config.DiskCache, config.DiskDiscard, config.Format)
if config.DetectZeroes != "off" {
driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
}
driveArgs = append(driveArgs, driveArgumentString)
}
}
driveArgs = append(driveArgs, driveArgumentString)
} else {
driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
if config.DetectZeroes != "off" {
driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
if config.DiskImage {
driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
if config.DetectZeroes != "off" {
driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
}
driveArgs = append(driveArgs, driveArgumentString)
} else {
diskFullPaths := state.Get("qemu_disk_paths").([]string)
for _, diskFullPath := range diskFullPaths {
driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", diskFullPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
if config.DetectZeroes != "off" {
driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
}
driveArgs = append(driveArgs, driveArgumentString)
}
}
driveArgs = append(driveArgs, driveArgumentString)
}
} else {
driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,cache=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.Format))

@ -142,6 +142,16 @@ Linux server and have not enabled X11 forwarding (`ssh -X`).
- `cpus` (number) - The number of cpus to use when building the VM.
The default is `1` CPU.
- `disk_additional_size` (array of strings) - Additional disks to create.
Uses `vm_name` as the disk name template and appends `-#` where `#` is
the position in the array. `#` starts at 1 since 0 is the default disk.
Each string represents the disk image size in bytes. Optional suffixes
'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),
'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are
supported. 'b' is ignored. Per qemu-img documentation.
Each additional disk uses the same disk parameters as the default disk.
Unset by default.
- `disk_cache` (string) - The cache mode to use for disk. Allowed values
include any of `writethrough`, `writeback`, `none`, `unsafe`
or `directsync`. By default, this is set to `writeback`.

Loading…
Cancel
Save