2019-05-31 08:27:41 -04:00
|
|
|
//go:generate struct-markdown
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
package qemu
|
|
|
|
|
|
|
|
import (
|
2019-03-22 09:53:28 -04:00
|
|
|
"context"
|
2013-09-02 23:23:52 -04:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2015-06-22 20:56:35 -04:00
|
|
|
"runtime"
|
2013-09-02 23:23:52 -04:00
|
|
|
"time"
|
2014-09-03 23:23:39 -04:00
|
|
|
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/common"
|
2018-04-18 19:18:12 -04:00
|
|
|
"github.com/hashicorp/packer/common/bootcommand"
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/helper/communicator"
|
|
|
|
"github.com/hashicorp/packer/helper/config"
|
2018-01-19 19:18:44 -05:00
|
|
|
"github.com/hashicorp/packer/helper/multistep"
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
"github.com/hashicorp/packer/template/interpolate"
|
2013-09-02 23:23:52 -04:00
|
|
|
)
|
|
|
|
|
2013-09-03 11:08:04 -04:00
|
|
|
const BuilderId = "transcend.qemu"
|
|
|
|
|
2014-09-02 12:31:53 -04:00
|
|
|
var accels = map[string]struct{}{
|
2016-11-01 17:08:04 -04:00
|
|
|
"none": {},
|
|
|
|
"kvm": {},
|
|
|
|
"tcg": {},
|
|
|
|
"xen": {},
|
2017-12-28 19:51:57 -05:00
|
|
|
"hax": {},
|
2018-04-25 10:50:58 -04:00
|
|
|
"hvf": {},
|
2018-12-26 01:49:42 -05:00
|
|
|
"whpx": {},
|
2014-09-02 12:31:53 -04:00
|
|
|
}
|
|
|
|
|
2013-09-03 11:08:04 -04:00
|
|
|
var netDevice = map[string]bool{
|
2014-09-02 12:31:53 -04:00
|
|
|
"ne2k_pci": true,
|
|
|
|
"i82551": true,
|
|
|
|
"i82557b": true,
|
|
|
|
"i82559er": true,
|
|
|
|
"rtl8139": true,
|
|
|
|
"e1000": true,
|
|
|
|
"pcnet": true,
|
|
|
|
"virtio": true,
|
|
|
|
"virtio-net": true,
|
2014-07-29 10:41:07 -04:00
|
|
|
"virtio-net-pci": true,
|
2014-09-02 12:31:53 -04:00
|
|
|
"usb-net": true,
|
|
|
|
"i82559a": true,
|
|
|
|
"i82559b": true,
|
|
|
|
"i82559c": true,
|
|
|
|
"i82550": true,
|
|
|
|
"i82562": true,
|
|
|
|
"i82557a": true,
|
|
|
|
"i82557c": true,
|
|
|
|
"i82801": true,
|
|
|
|
"vmxnet3": true,
|
|
|
|
"i82558a": true,
|
|
|
|
"i82558b": true,
|
2013-09-03 11:08:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var diskInterface = map[string]bool{
|
2015-09-08 06:58:21 -04:00
|
|
|
"ide": true,
|
|
|
|
"scsi": true,
|
|
|
|
"virtio": true,
|
|
|
|
"virtio-scsi": true,
|
2013-09-03 11:08:04 -04:00
|
|
|
}
|
2013-09-02 23:23:52 -04:00
|
|
|
|
2014-10-05 17:09:45 -04:00
|
|
|
var diskCache = map[string]bool{
|
|
|
|
"writethrough": true,
|
|
|
|
"writeback": true,
|
|
|
|
"none": true,
|
|
|
|
"unsafe": true,
|
|
|
|
"directsync": true,
|
|
|
|
}
|
|
|
|
|
2015-05-17 13:48:58 -04:00
|
|
|
var diskDiscard = map[string]bool{
|
|
|
|
"unmap": true,
|
|
|
|
"ignore": true,
|
|
|
|
}
|
|
|
|
|
2018-10-10 07:38:59 -04:00
|
|
|
var diskDZeroes = map[string]bool{
|
2018-10-10 09:16:23 -04:00
|
|
|
"unmap": true,
|
|
|
|
"on": true,
|
|
|
|
"off": true,
|
2018-10-10 07:38:59 -04:00
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
type Builder struct {
|
2015-05-27 16:39:43 -04:00
|
|
|
config Config
|
2013-09-02 23:23:52 -04:00
|
|
|
runner multistep.Runner
|
|
|
|
}
|
|
|
|
|
2015-05-27 16:39:43 -04:00
|
|
|
type Config struct {
|
2018-04-18 19:18:12 -04:00
|
|
|
common.PackerConfig `mapstructure:",squash"`
|
|
|
|
common.HTTPConfig `mapstructure:",squash"`
|
|
|
|
common.ISOConfig `mapstructure:",squash"`
|
|
|
|
bootcommand.VNCConfig `mapstructure:",squash"`
|
|
|
|
Comm communicator.Config `mapstructure:",squash"`
|
|
|
|
common.FloppyConfig `mapstructure:",squash"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// Use iso from provided url. Qemu must support
|
|
|
|
// curl block device. This defaults to false.
|
|
|
|
ISOSkipCache bool `mapstructure:"iso_skip_cache" required:"false"`
|
|
|
|
// The accelerator type to use when running the VM.
|
|
|
|
// This may be none, kvm, tcg, hax, hvf, whpx, or xen. The appropriate
|
|
|
|
// software must have already been installed on your build machine to use the
|
|
|
|
// accelerator you specified. When no accelerator is specified, Packer will try
|
|
|
|
// to use kvm if it is available but will default to tcg otherwise.
|
|
|
|
Accelerator string `mapstructure:"accelerator" required:"false"`
|
|
|
|
// The number of cpus to use when building the VM.
|
|
|
|
// The default is 1 CPU.
|
|
|
|
CpuCount int `mapstructure:"cpus" required:"false"`
|
|
|
|
// The interface to use for the disk. Allowed
|
|
|
|
// values include any of ide, scsi, virtio or virtio-scsi*. Note
|
|
|
|
// also that any boot commands or kickstart type scripts must have proper
|
|
|
|
// adjustments for resulting device names. The Qemu builder uses virtio by
|
|
|
|
// default.
|
|
|
|
DiskInterface string `mapstructure:"disk_interface" required:"false"`
|
|
|
|
// The size, in megabytes, of the hard disk to create
|
|
|
|
// for the VM. By default, this is 40960 (40 GB).
|
|
|
|
DiskSize uint `mapstructure:"disk_size" required:"false"`
|
|
|
|
// 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.
|
|
|
|
DiskCache string `mapstructure:"disk_cache" required:"false"`
|
|
|
|
// The discard mode to use for disk. Allowed values
|
|
|
|
// include any of unmap or ignore. By default, this is set to ignore.
|
|
|
|
DiskDiscard string `mapstructure:"disk_discard" required:"false"`
|
|
|
|
// The detect-zeroes mode to use for disk.
|
|
|
|
// Allowed values include any of unmap, on or off. Defaults to off.
|
|
|
|
// When the value is "off" we don't set the flag in the qemu command, so that
|
|
|
|
// Packer still works with old versions of QEMU that don't have this option.
|
|
|
|
DetectZeroes string `mapstructure:"disk_detect_zeroes" required:"false"`
|
|
|
|
// Packer compacts the QCOW2 image using
|
|
|
|
// qemu-img convert. Set this option to true to disable compacting.
|
|
|
|
// Defaults to false.
|
|
|
|
SkipCompaction bool `mapstructure:"skip_compaction" required:"false"`
|
|
|
|
// Apply compression to the QCOW2 disk file
|
|
|
|
// using qemu-img convert. Defaults to false.
|
|
|
|
DiskCompression bool `mapstructure:"disk_compression" required:"false"`
|
|
|
|
// Either qcow2 or raw, this specifies the output
|
|
|
|
// format of the virtual machine image. This defaults to qcow2.
|
|
|
|
Format string `mapstructure:"format" required:"false"`
|
|
|
|
// Packer defaults to building QEMU 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.
|
|
|
|
Headless bool `mapstructure:"headless" required:"false"`
|
|
|
|
// Packer defaults to building from an ISO file, this
|
|
|
|
// parameter controls whether the ISO URL supplied is actually a bootable
|
|
|
|
// QEMU image. When this value is set to true, the machine will either clone
|
|
|
|
// the source or use it as a backing file (if use_backing_file is true);
|
|
|
|
// then, it will resize the image according to disk_size and boot it.
|
|
|
|
DiskImage bool `mapstructure:"disk_image" required:"false"`
|
|
|
|
// Only applicable when disk_image is true
|
|
|
|
// and format is qcow2, set this option to true to create a new QCOW2
|
|
|
|
// file that uses the file located at iso_url as a backing file. The new file
|
|
|
|
// will only contain blocks that have changed compared to the backing file, so
|
|
|
|
// enabling this option can significantly reduce disk usage.
|
|
|
|
UseBackingFile bool `mapstructure:"use_backing_file" required:"false"`
|
|
|
|
// The type of machine emulation to use. Run your
|
|
|
|
// qemu binary with the flags -machine help to list available types for
|
|
|
|
// your system. This defaults to pc.
|
|
|
|
MachineType string `mapstructure:"machine_type" required:"false"`
|
|
|
|
// The amount of memory to use when building the VM
|
|
|
|
// in megabytes. This defaults to 512 megabytes.
|
|
|
|
MemorySize int `mapstructure:"memory" required:"false"`
|
|
|
|
// The driver to use for the network interface. Allowed
|
|
|
|
// values ne2k_pci, i82551, i82557b, i82559er, rtl8139, e1000,
|
|
|
|
// pcnet, virtio, virtio-net, virtio-net-pci, usb-net, i82559a,
|
|
|
|
// i82559b, i82559c, i82550, i82562, i82557a, i82557c, i82801,
|
|
|
|
// vmxnet3, i82558a or i82558b. The Qemu builder uses virtio-net by
|
|
|
|
// default.
|
|
|
|
NetDevice string `mapstructure:"net_device" required:"false"`
|
|
|
|
// This is the path to the directory where the
|
|
|
|
// resulting virtual machine will be created. This may be relative or absolute.
|
|
|
|
// If relative, the path is relative to the working directory when packer
|
|
|
|
// is executed. This directory must not exist or be empty prior to running
|
|
|
|
// the builder. By default this is output-BUILDNAME where "BUILDNAME" is the
|
|
|
|
// name of the build.
|
|
|
|
OutputDir string `mapstructure:"output_directory" required:"false"`
|
|
|
|
// Allows complete control over the
|
|
|
|
// qemu command line (though not, at this time, qemu-img). Each array of
|
|
|
|
// strings makes up a command line switch that overrides matching default
|
|
|
|
// switch/value pairs. Any value specified as an empty string is ignored. All
|
|
|
|
// values after the switch are concatenated with no separator.
|
|
|
|
QemuArgs [][]string `mapstructure:"qemuargs" required:"false"`
|
|
|
|
// The name of the Qemu binary to look for. This
|
|
|
|
// defaults to qemu-system-x86_64, but may need to be changed for
|
|
|
|
// some platforms. For example qemu-kvm, or qemu-system-i386 may be a
|
|
|
|
// better choice for some systems.
|
|
|
|
QemuBinary string `mapstructure:"qemu_binary" required:"false"`
|
|
|
|
// 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 unless a
|
|
|
|
// shutdown command takes place inside script so this may safely be omitted. It
|
|
|
|
// is important to add a shutdown_command. By default Packer halts the virtual
|
|
|
|
// machine and the file system may not be sync'd. Thus, changes made in a
|
|
|
|
// provisioner might not be saved. If one or more scripts require a reboot it is
|
|
|
|
// suggested to leave this blank since reboots may fail and specify the final
|
|
|
|
// shutdown command in your last script.
|
|
|
|
ShutdownCommand string `mapstructure:"shutdown_command" required:"false"`
|
|
|
|
// The minimum and
|
|
|
|
// maximum port to use for the SSH port on the host machine which is forwarded
|
|
|
|
// to the SSH port on the guest machine. Because Packer often runs in parallel,
|
|
|
|
// Packer will choose a randomly available port in this range to use as the
|
|
|
|
// host port. By default this is 2222 to 4444.
|
|
|
|
SSHHostPortMin int `mapstructure:"ssh_host_port_min" required:"false"`
|
2019-03-19 09:47:21 -04:00
|
|
|
SSHHostPortMax int `mapstructure:"ssh_host_port_max"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// If true, do not pass a -display option
|
|
|
|
// to qemu, allowing it to choose the default. This may be needed when running
|
|
|
|
// under macOS, and getting errors about sdl not being available.
|
|
|
|
UseDefaultDisplay bool `mapstructure:"use_default_display" required:"false"`
|
|
|
|
// The IP address that should be
|
|
|
|
// binded to for VNC. By default packer will use 127.0.0.1 for this. If you
|
|
|
|
// wish to bind to all interfaces use 0.0.0.0.
|
|
|
|
VNCBindAddress string `mapstructure:"vnc_bind_address" required:"false"`
|
|
|
|
// The minimum and maximum port
|
|
|
|
// to use for VNC access to the virtual machine. The builder uses VNC to type
|
|
|
|
// the initial boot_command. Because Packer generally runs in parallel,
|
|
|
|
// Packer uses a randomly chosen port in this range that appears available. By
|
|
|
|
// default this is 5900 to 6000. The minimum and maximum ports are inclusive.
|
|
|
|
VNCPortMin int `mapstructure:"vnc_port_min" required:"false"`
|
2019-03-19 09:47:21 -04:00
|
|
|
VNCPortMax int `mapstructure:"vnc_port_max"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// This is the name of the image (QCOW2 or IMG) file for
|
|
|
|
// the new virtual machine. By default this is packer-BUILDNAME, where
|
|
|
|
// "BUILDNAME" is the name of the build. Currently, no file extension will be
|
|
|
|
// used unless it is specified in this option.
|
|
|
|
VMName string `mapstructure:"vm_name" required:"false"`
|
2014-02-21 18:18:03 -05:00
|
|
|
|
2015-06-13 18:47:59 -04:00
|
|
|
// These are deprecated, but we keep them around for BC
|
|
|
|
// TODO(@mitchellh): remove
|
|
|
|
SSHWaitTimeout time.Duration `mapstructure:"ssh_wait_timeout"`
|
|
|
|
|
2014-02-21 18:18:03 -05:00
|
|
|
// TODO(mitchellh): deprecate
|
2014-02-21 19:48:30 -05:00
|
|
|
RunOnce bool `mapstructure:"run_once"`
|
2019-05-28 11:50:58 -04:00
|
|
|
// The amount of time to wait after executing the
|
|
|
|
// shutdown_command for the virtual machine to actually shut down. If it
|
|
|
|
// doesn't shut down in this time, it is an error. By default, the timeout is
|
|
|
|
// 5m or five minutes.
|
|
|
|
RawShutdownTimeout string `mapstructure:"shutdown_timeout" required:"false"`
|
2013-09-02 23:23:52 -04:00
|
|
|
|
|
|
|
shutdownTimeout time.Duration ``
|
2015-05-27 16:39:43 -04:00
|
|
|
ctx interpolate.Context
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:44:38 -05:00
|
|
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
2015-05-27 16:39:43 -04:00
|
|
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
2015-06-22 12:22:42 -04:00
|
|
|
Interpolate: true,
|
|
|
|
InterpolateContext: &b.config.ctx,
|
2015-05-27 16:39:43 -04:00
|
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
|
|
|
Exclude: []string{
|
|
|
|
"boot_command",
|
|
|
|
"qemuargs",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, raws...)
|
2013-09-02 23:23:52 -04:00
|
|
|
if err != nil {
|
2013-11-05 18:44:38 -05:00
|
|
|
return nil, err
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2016-07-26 15:42:04 -04:00
|
|
|
var errs *packer.MultiError
|
|
|
|
warnings := make([]string, 0)
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if b.config.DiskSize == 0 {
|
2017-11-15 14:47:46 -05:00
|
|
|
b.config.DiskSize = 40960
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2014-10-05 17:09:45 -04:00
|
|
|
if b.config.DiskCache == "" {
|
|
|
|
b.config.DiskCache = "writeback"
|
|
|
|
}
|
|
|
|
|
2015-05-17 13:48:58 -04:00
|
|
|
if b.config.DiskDiscard == "" {
|
|
|
|
b.config.DiskDiscard = "ignore"
|
|
|
|
}
|
|
|
|
|
2018-10-10 07:38:59 -04:00
|
|
|
if b.config.DetectZeroes == "" {
|
|
|
|
b.config.DetectZeroes = "off"
|
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if b.config.Accelerator == "" {
|
2015-06-22 20:56:35 -04:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
b.config.Accelerator = "tcg"
|
|
|
|
} else {
|
2016-05-20 01:53:53 -04:00
|
|
|
// /dev/kvm is a kernel module that may be loaded if kvm is
|
|
|
|
// installed and the host supports VT-x extensions. To make sure
|
|
|
|
// this will actually work we need to os.Open() it. If os.Open fails
|
|
|
|
// the kernel module was not installed or loaded correctly.
|
2016-05-17 09:26:59 -04:00
|
|
|
if fp, err := os.Open("/dev/kvm"); err != nil {
|
|
|
|
b.config.Accelerator = "tcg"
|
|
|
|
} else {
|
|
|
|
fp.Close()
|
|
|
|
b.config.Accelerator = "kvm"
|
|
|
|
}
|
2015-06-22 20:56:35 -04:00
|
|
|
}
|
2016-05-17 09:26:59 -04:00
|
|
|
log.Printf("use detected accelerator: %s", b.config.Accelerator)
|
|
|
|
} else {
|
|
|
|
log.Printf("use specified accelerator: %s", b.config.Accelerator)
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2014-08-12 03:02:03 -04:00
|
|
|
if b.config.MachineType == "" {
|
2014-10-03 11:37:03 -04:00
|
|
|
b.config.MachineType = "pc"
|
2014-08-12 03:02:03 -04:00
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if b.config.OutputDir == "" {
|
|
|
|
b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName)
|
|
|
|
}
|
|
|
|
|
2014-02-21 18:22:58 -05:00
|
|
|
if b.config.QemuBinary == "" {
|
|
|
|
b.config.QemuBinary = "qemu-system-x86_64"
|
|
|
|
}
|
|
|
|
|
2018-12-29 15:58:05 -05:00
|
|
|
if b.config.MemorySize < 10 {
|
|
|
|
log.Printf("MemorySize %d is too small, using default: 512", b.config.MemorySize)
|
|
|
|
b.config.MemorySize = 512
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.config.CpuCount < 1 {
|
|
|
|
log.Printf("CpuCount %d too small, using default: 1", b.config.CpuCount)
|
|
|
|
b.config.CpuCount = 1
|
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if b.config.SSHHostPortMin == 0 {
|
|
|
|
b.config.SSHHostPortMin = 2222
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.config.SSHHostPortMax == 0 {
|
|
|
|
b.config.SSHHostPortMax = 4444
|
|
|
|
}
|
|
|
|
|
2016-05-25 05:10:12 -04:00
|
|
|
if b.config.VNCBindAddress == "" {
|
|
|
|
b.config.VNCBindAddress = "127.0.0.1"
|
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if b.config.VNCPortMin == 0 {
|
|
|
|
b.config.VNCPortMin = 5900
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.config.VNCPortMax == 0 {
|
|
|
|
b.config.VNCPortMax = 6000
|
|
|
|
}
|
|
|
|
|
2015-08-21 19:46:29 -04:00
|
|
|
if b.config.VMName == "" {
|
|
|
|
b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName)
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2015-08-21 19:46:29 -04:00
|
|
|
if b.config.Format == "" {
|
|
|
|
b.config.Format = "qcow2"
|
2015-08-18 20:44:17 -04:00
|
|
|
}
|
|
|
|
|
2016-07-26 15:42:04 -04:00
|
|
|
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
2018-05-01 15:40:00 -04:00
|
|
|
errs = packer.MultiErrorAppend(errs, b.config.VNCConfig.Prepare(&b.config.ctx)...)
|
2013-12-06 19:20:25 -05:00
|
|
|
|
2013-09-03 11:08:04 -04:00
|
|
|
if b.config.NetDevice == "" {
|
2013-10-09 08:11:10 -04:00
|
|
|
b.config.NetDevice = "virtio-net"
|
2013-09-03 11:08:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if b.config.DiskInterface == "" {
|
|
|
|
b.config.DiskInterface = "virtio"
|
|
|
|
}
|
|
|
|
|
2015-06-13 18:47:59 -04:00
|
|
|
// TODO: backwards compatibility, write fixer instead
|
|
|
|
if b.config.SSHWaitTimeout != 0 {
|
|
|
|
b.config.Comm.SSHTimeout = b.config.SSHWaitTimeout
|
|
|
|
}
|
|
|
|
|
2016-01-25 04:01:37 -05:00
|
|
|
if b.config.ISOSkipCache {
|
|
|
|
b.config.ISOChecksumType = "none"
|
|
|
|
}
|
|
|
|
|
2015-10-20 19:27:47 -04:00
|
|
|
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
|
|
|
|
warnings = append(warnings, isoWarnings...)
|
|
|
|
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
|
|
|
|
2015-11-01 17:29:24 -05:00
|
|
|
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
2015-06-13 18:47:59 -04:00
|
|
|
if es := b.config.Comm.Prepare(&b.config.ctx); len(es) > 0 {
|
|
|
|
errs = packer.MultiErrorAppend(errs, es...)
|
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if !(b.config.Format == "qcow2" || b.config.Format == "raw") {
|
|
|
|
errs = packer.MultiErrorAppend(
|
2013-12-04 14:05:07 -05:00
|
|
|
errs, errors.New("invalid format, only 'qcow2' or 'raw' are allowed"))
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2015-10-29 06:54:25 -04:00
|
|
|
if b.config.Format != "qcow2" {
|
|
|
|
b.config.SkipCompaction = true
|
|
|
|
b.config.DiskCompression = false
|
|
|
|
}
|
|
|
|
|
2018-05-02 00:56:13 -04:00
|
|
|
if b.config.UseBackingFile && !(b.config.DiskImage && b.config.Format == "qcow2") {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("use_backing_file can only be enabled for QCOW2 images and when disk_image is true"))
|
|
|
|
}
|
|
|
|
|
2014-09-02 12:31:53 -04:00
|
|
|
if _, ok := accels[b.config.Accelerator]; !ok {
|
2013-09-02 23:23:52 -04:00
|
|
|
errs = packer.MultiErrorAppend(
|
2018-12-26 01:49:42 -05:00
|
|
|
errs, errors.New("invalid accelerator, only 'kvm', 'tcg', 'xen', 'hax', 'hvf', 'whpx', or 'none' are allowed"))
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2013-09-03 11:08:04 -04:00
|
|
|
if _, ok := netDevice[b.config.NetDevice]; !ok {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("unrecognized network device type"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := diskInterface[b.config.DiskInterface]; !ok {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("unrecognized disk interface type"))
|
|
|
|
}
|
|
|
|
|
2014-10-05 17:09:45 -04:00
|
|
|
if _, ok := diskCache[b.config.DiskCache]; !ok {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("unrecognized disk cache type"))
|
|
|
|
}
|
|
|
|
|
2015-05-17 13:48:58 -04:00
|
|
|
if _, ok := diskDiscard[b.config.DiskDiscard]; !ok {
|
|
|
|
errs = packer.MultiErrorAppend(
|
2018-04-20 07:17:40 -04:00
|
|
|
errs, errors.New("unrecognized disk discard type"))
|
2015-05-17 13:48:58 -04:00
|
|
|
}
|
|
|
|
|
2018-10-10 07:38:59 -04:00
|
|
|
if _, ok := diskDZeroes[b.config.DetectZeroes]; !ok {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("unrecognized disk detect zeroes setting"))
|
|
|
|
}
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
if !b.config.PackerForce {
|
|
|
|
if _, err := os.Stat(b.config.OutputDir); err == nil {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs,
|
|
|
|
fmt.Errorf("Output directory '%s' already exists. It must not exist.", b.config.OutputDir))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.config.RawShutdownTimeout == "" {
|
|
|
|
b.config.RawShutdownTimeout = "5m"
|
|
|
|
}
|
|
|
|
|
|
|
|
b.config.shutdownTimeout, err = time.ParseDuration(b.config.RawShutdownTimeout)
|
|
|
|
if err != nil {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.config.SSHHostPortMin > b.config.SSHHostPortMax {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
|
|
|
|
}
|
2019-03-19 10:21:09 -04:00
|
|
|
if b.config.SSHHostPortMin < 0 {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, errors.New("ssh_host_port_min must be positive"))
|
|
|
|
}
|
2013-09-02 23:23:52 -04:00
|
|
|
|
|
|
|
if b.config.VNCPortMin > b.config.VNCPortMax {
|
|
|
|
errs = packer.MultiErrorAppend(
|
|
|
|
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
|
|
|
}
|
|
|
|
|
2013-12-06 19:20:25 -05:00
|
|
|
if b.config.QemuArgs == nil {
|
|
|
|
b.config.QemuArgs = make([][]string, 0)
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if errs != nil && len(errs.Errors) > 0 {
|
2014-10-31 09:07:04 -04:00
|
|
|
return warnings, errs
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2014-10-31 09:07:04 -04:00
|
|
|
return warnings, nil
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2019-03-22 09:53:28 -04:00
|
|
|
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
2013-09-02 23:23:52 -04:00
|
|
|
// Create the driver that we'll use to communicate with Qemu
|
2014-01-24 13:01:42 -05:00
|
|
|
driver, err := b.newDriver(b.config.QemuBinary)
|
2013-09-02 23:23:52 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed creating Qemu driver: %s", err)
|
|
|
|
}
|
|
|
|
|
2014-11-06 15:13:09 -05:00
|
|
|
steprun := &stepRun{}
|
|
|
|
if !b.config.DiskImage {
|
|
|
|
steprun.BootDrive = "once=d"
|
|
|
|
steprun.Message = "Starting VM, booting from CD-ROM"
|
|
|
|
} else {
|
|
|
|
steprun.BootDrive = "c"
|
|
|
|
steprun.Message = "Starting VM, booting disk image"
|
|
|
|
}
|
|
|
|
|
2016-01-25 04:01:37 -05:00
|
|
|
steps := []multistep.Step{}
|
|
|
|
if !b.config.ISOSkipCache {
|
|
|
|
steps = append(steps, &common.StepDownload{
|
2013-09-02 23:23:52 -04:00
|
|
|
Checksum: b.config.ISOChecksum,
|
|
|
|
ChecksumType: b.config.ISOChecksumType,
|
|
|
|
Description: "ISO",
|
2016-12-17 05:49:54 -05:00
|
|
|
Extension: b.config.TargetExtension,
|
2013-09-02 23:23:52 -04:00
|
|
|
ResultKey: "iso_path",
|
2015-07-28 07:45:02 -04:00
|
|
|
TargetPath: b.config.TargetPath,
|
2015-10-20 19:27:47 -04:00
|
|
|
Url: b.config.ISOUrls,
|
2013-09-02 23:23:52 -04:00
|
|
|
},
|
2016-01-25 04:01:37 -05:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
steps = append(steps, &stepSetISO{
|
|
|
|
ResultKey: "iso_path",
|
|
|
|
Url: b.config.ISOUrls,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
steps = append(steps, new(stepPrepareOutputDir),
|
2013-12-06 19:20:25 -05:00
|
|
|
&common.StepCreateFloppy{
|
2016-10-11 17:43:50 -04:00
|
|
|
Files: b.config.FloppyConfig.FloppyFiles,
|
2016-09-28 00:31:42 -04:00
|
|
|
Directories: b.config.FloppyConfig.FloppyDirectories,
|
2013-12-06 19:20:25 -05:00
|
|
|
},
|
2013-09-02 23:23:52 -04:00
|
|
|
new(stepCreateDisk),
|
2014-07-17 10:01:19 -04:00
|
|
|
new(stepCopyDisk),
|
|
|
|
new(stepResizeDisk),
|
2015-11-01 17:29:24 -05:00
|
|
|
&common.StepHTTPServer{
|
|
|
|
HTTPDir: b.config.HTTPDir,
|
|
|
|
HTTPPortMin: b.config.HTTPPortMin,
|
|
|
|
HTTPPortMax: b.config.HTTPPortMax,
|
|
|
|
},
|
2016-07-06 11:52:40 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
if b.config.Comm.Type != "none" {
|
|
|
|
steps = append(steps,
|
|
|
|
new(stepForwardSSH),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
steps = append(steps,
|
2013-09-02 23:23:52 -04:00
|
|
|
new(stepConfigureVNC),
|
2014-11-06 15:13:09 -05:00
|
|
|
steprun,
|
2014-01-30 02:46:54 -05:00
|
|
|
&stepTypeBootCommand{},
|
2016-07-06 11:52:40 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
if b.config.Comm.Type != "none" {
|
|
|
|
steps = append(steps,
|
|
|
|
&communicator.StepConnect{
|
|
|
|
Config: &b.config.Comm,
|
|
|
|
Host: commHost,
|
2018-08-22 11:02:23 -04:00
|
|
|
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
2016-07-06 11:52:40 -04:00
|
|
|
SSHPort: commPort,
|
2016-12-23 18:15:51 -05:00
|
|
|
WinRMPort: commPort,
|
2016-07-06 11:52:40 -04:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
steps = append(steps,
|
2013-09-02 23:23:52 -04:00
|
|
|
new(common.StepProvision),
|
2016-07-06 11:52:40 -04:00
|
|
|
)
|
2018-09-14 14:03:23 -04:00
|
|
|
|
|
|
|
steps = append(steps,
|
|
|
|
&common.StepCleanupTempKeys{
|
|
|
|
Comm: &b.config.Comm,
|
|
|
|
},
|
|
|
|
)
|
2016-07-06 11:52:40 -04:00
|
|
|
steps = append(steps,
|
2013-09-02 23:23:52 -04:00
|
|
|
new(stepShutdown),
|
2016-07-06 11:52:40 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
steps = append(steps,
|
2015-10-29 06:54:25 -04:00
|
|
|
new(stepConvertDisk),
|
2016-01-25 04:01:37 -05:00
|
|
|
)
|
2013-09-02 23:23:52 -04:00
|
|
|
|
|
|
|
// Setup the state bag
|
|
|
|
state := new(multistep.BasicStateBag)
|
|
|
|
state.Put("config", &b.config)
|
2016-05-17 17:14:50 -04:00
|
|
|
state.Put("debug", b.config.PackerDebug)
|
2013-09-02 23:23:52 -04:00
|
|
|
state.Put("driver", driver)
|
|
|
|
state.Put("hook", hook)
|
|
|
|
state.Put("ui", ui)
|
|
|
|
|
|
|
|
// Run
|
2016-09-13 20:04:18 -04:00
|
|
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
2019-03-22 09:53:28 -04:00
|
|
|
b.runner.Run(ctx, state)
|
2013-09-02 23:23:52 -04:00
|
|
|
|
|
|
|
// If there was an error, return that
|
|
|
|
if rawErr, ok := state.GetOk("error"); ok {
|
|
|
|
return nil, rawErr.(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we were interrupted or cancelled, then just exit.
|
|
|
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
|
|
|
return nil, errors.New("Build was cancelled.")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
|
|
|
return nil, errors.New("Build was halted.")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compile the artifact list
|
|
|
|
files := make([]string, 0, 5)
|
|
|
|
visit := func(path string, info os.FileInfo, err error) error {
|
2017-01-26 19:32:21 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-09-02 23:23:52 -04:00
|
|
|
if !info.IsDir() {
|
|
|
|
files = append(files, path)
|
|
|
|
}
|
|
|
|
|
2017-01-26 19:32:21 -05:00
|
|
|
return nil
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := filepath.Walk(b.config.OutputDir, visit); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
artifact := &Artifact{
|
2014-10-28 11:43:19 -04:00
|
|
|
dir: b.config.OutputDir,
|
|
|
|
f: files,
|
2014-01-05 20:02:30 -05:00
|
|
|
state: make(map[string]interface{}),
|
2013-09-02 23:23:52 -04:00
|
|
|
}
|
|
|
|
|
2014-01-05 20:02:30 -05:00
|
|
|
artifact.state["diskName"] = state.Get("disk_filename").(string)
|
|
|
|
artifact.state["diskType"] = b.config.Format
|
|
|
|
artifact.state["diskSize"] = uint64(b.config.DiskSize)
|
|
|
|
artifact.state["domainType"] = b.config.Accelerator
|
|
|
|
|
2013-09-02 23:23:52 -04:00
|
|
|
return artifact, nil
|
|
|
|
}
|
|
|
|
|
2014-01-24 13:01:42 -05:00
|
|
|
func (b *Builder) newDriver(qemuBinary string) (Driver, error) {
|
|
|
|
qemuPath, err := exec.LookPath(qemuBinary)
|
2013-09-02 23:23:52 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuImgPath, err := exec.LookPath("qemu-img")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("Qemu path: %s, Qemu Image page: %s", qemuPath, qemuImgPath)
|
2013-11-06 00:43:20 -05:00
|
|
|
driver := &QemuDriver{
|
|
|
|
QemuPath: qemuPath,
|
|
|
|
QemuImgPath: qemuImgPath,
|
|
|
|
}
|
2013-09-02 23:23:52 -04:00
|
|
|
|
|
|
|
if err := driver.Verify(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return driver, nil
|
|
|
|
}
|