QEMU: Initial QMP support

This commit is contained in:
Jayson Cofell 2019-07-03 18:33:59 -06:00
parent ba66d5d857
commit afe9ba2869
5 changed files with 98 additions and 0 deletions

View File

@ -117,6 +117,8 @@ type Config struct {
OutputDir string `mapstructure:"output_directory"` OutputDir string `mapstructure:"output_directory"`
QemuArgs [][]string `mapstructure:"qemuargs"` QemuArgs [][]string `mapstructure:"qemuargs"`
QemuBinary string `mapstructure:"qemu_binary"` QemuBinary string `mapstructure:"qemu_binary"`
QMPEnable bool `mapstructure:"qmp_enable"`
QMPSocketPath string `mapstructure:"qmp_socket_path"`
ShutdownCommand string `mapstructure:"shutdown_command"` ShutdownCommand string `mapstructure:"shutdown_command"`
SSHHostPortMin int `mapstructure:"ssh_host_port_min"` SSHHostPortMin int `mapstructure:"ssh_host_port_min"`
SSHHostPortMax int `mapstructure:"ssh_host_port_max"` SSHHostPortMax int `mapstructure:"ssh_host_port_max"`
@ -344,6 +346,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max")) errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
} }
if b.config.SSHHostPortMin < 0 { if b.config.SSHHostPortMin < 0 {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, errors.New("ssh_host_port_min must be positive")) errs, errors.New("ssh_host_port_min must be positive"))
@ -354,6 +357,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max")) errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
} }
if b.config.QMPEnable && b.config.QMPSocketPath == "" {
socketName := fmt.Sprintf("%s.monitor", b.config.VMName)
b.config.QMPSocketPath = filepath.Join(b.config.OutputDir, socketName)
}
if b.config.QemuArgs == nil { if b.config.QemuArgs == nil {
b.config.QemuArgs = make([][]string, 0) b.config.QemuArgs = make([][]string, 0)
} }
@ -425,6 +433,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
steps = append(steps, steps = append(steps,
new(stepConfigureVNC), new(stepConfigureVNC),
steprun, steprun,
new(stepConfigureQMP),
&stepTypeBootCommand{}, &stepTypeBootCommand{},
) )

View File

@ -598,3 +598,25 @@ func TestBuilderPrepare_QemuArgs(t *testing.T) {
t.Fatalf("bad: %#v", b.config.QemuArgs) t.Fatalf("bad: %#v", b.config.QemuArgs)
} }
} }
func TestBuilderPrepare_QMP(t *testing.T) {
var b Builder
config := testConfig()
// QMP Defaults
config["qmp_enable"] = true
config["output_directory"] = "not-a-real-directory"
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)
}
expected := "not-a-real-directory/packer-foo.monitor"
if !reflect.DeepEqual(b.config.QMPSocketPath, expected) {
t.Fatalf("Bad QMP socket Path: %s", b.config.QMPSocketPath)
}
}

View File

@ -0,0 +1,56 @@
package qemu
import (
"context"
"fmt"
"log"
"time"
"github.com/digitalocean/go-qemu/qmp"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// This step configures the VM to enable the QMP listener.
//
// Uses:
// config *config
// ui packer.Ui
//
// Produces:
type stepConfigureQMP struct {
monitor *qmp.SocketMonitor
}
func (s *stepConfigureQMP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
if !config.QMPEnable {
return multistep.ActionContinue
}
msg := fmt.Sprintf("Opening QMP socket at: %s", config.QMPSocketPath)
ui.Say(msg)
log.Print(msg)
// Open QMP socket
var err error
s.monitor, err = qmp.NewSocketMonitor("unix", config.QMPSocketPath, 2*time.Second)
if err != nil {
err := fmt.Errorf("Error opening QMP socket: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
QMPMonitor := s.monitor
log.Printf("QMP socket open SUCCESS")
state.Put("qmp_monitor", QMPMonitor)
return multistep.ActionContinue
}
func (s *stepConfigureQMP) Cleanup(multistep.StateBag) {
}

View File

@ -85,6 +85,10 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0") defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0")
} }
if config.QMPEnable {
defaultArgs["-qmp"] = fmt.Sprintf("unix:%s,server,nowait", config.QMPSocketPath)
}
rawVersion, err := driver.Version() rawVersion, err := driver.Version()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -282,6 +282,13 @@ Linux server and have not enabled X11 forwarding (`ssh -X`).
switch/value pairs. Any value specified as an empty string is ignored. All switch/value pairs. Any value specified as an empty string is ignored. All
values after the switch are concatenated with no separator. values after the switch are concatenated with no separator.
- `qmp_enable` (bool) - Enable QMP socket. Location is specified by
`qmp_socket_path`.
Defaults to false.
- `qmp_socket_path` (string) - QMP Socket Path when `qmp_enable` is true.
Defaults to `output_directory`/`vm_name`.monitor.
~&gt; **Warning:** The qemu command line allows extreme flexibility, so beware ~&gt; **Warning:** The qemu command line allows extreme flexibility, so beware
of conflicting arguments causing failures of your run. For instance, using of conflicting arguments causing failures of your run. For instance, using
--no-acpi could break the ability to send power signal type commands (e.g., --no-acpi could break the ability to send power signal type commands (e.g.,