qemu: Get rid of the Driver structure

By adding QemuParams() to the Device interface, we can get rid of the
driver structure and simplify further the appendDevices() routine.

With that implementation we can generate the following qemu parameters:

"-device virtio-9p-pci,fsdev=foo,mount_tag=rootfs -fsdev local,id=foo,path=/bar/foo,security-model=none"

from these single structures:

        fsdev := FSDevice{
                Driver:        Virtio9P
                FSDriver:      Local,
                ID:            "foo",
                Path:          "/bar/foo",
                MountTag:      "rootfs",
                SecurityModel: None,
        }

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2016-09-17 00:25:50 +02:00
parent cc9cb33a5d
commit 6d7dfa04bf
2 changed files with 217 additions and 255 deletions

415
qemu.go
View File

@ -46,9 +46,10 @@ type Machine struct {
// Device is the qemu device interface.
type Device interface {
Valid() bool
QemuParams() []string
}
// DeviceDriver is the Device driver string.
// DeviceDriver is the device driver string.
type DeviceDriver string
const (
@ -68,74 +69,6 @@ const (
Console = "virtconsole"
)
// Driver describes a driver to be used by qemu.
type Driver struct {
// Driver is the qemu device driver
Driver DeviceDriver
// ID is the user defined device ID.
ID string
// Device is the device to be associated with this driver.
Device Device
}
func (driver Driver) deviceMatch() bool {
switch driver.Device.(type) {
case Object:
if driver.Driver != NVDIMM {
return false
}
case FSDevice:
if driver.Driver != Virtio9P {
return false
}
case SerialDevice:
if driver.Driver != VirtioSerial {
return false
}
case CharDevice:
if driver.Driver != Console {
return false
}
case NetDevice:
if driver.Driver != VirtioNet {
return false
}
default:
return false
}
return true
}
// Valid returns true if the Driver structure is valid and complete.
func (driver Driver) Valid() bool {
switch driver.Driver {
case NVDIMM:
case Console:
case VirtioSerial:
if driver.ID == "" || driver.Device.Valid() == false {
return false
}
case Virtio9P:
case VirtioNet:
return driver.Device.Valid()
default:
return false
}
// Verify that the Device type matches the Driver.
return driver.deviceMatch()
}
// ObjectType is a string representing a qemu object type.
type ObjectType string
@ -146,12 +79,18 @@ const (
// Object is a qemu object representation.
type Object struct {
// Driver is the qemu device driver
Driver DeviceDriver
// Type is the qemu object type.
Type ObjectType
// ID is the user defined object ID.
ID string
// DeviceID is the user defined device ID.
DeviceID string
// MemPath is the object's memory path.
// This is only relevant for memory objects
MemPath string
@ -167,11 +106,42 @@ func (object Object) Valid() bool {
if object.ID == "" || object.MemPath == "" || object.Size == 0 {
return false
}
default:
return false
}
return true
}
// QemuParams returns the qemu parameters built out of this Object device.
func (object Object) QemuParams() []string {
var objectParams []string
var deviceParams []string
var qemuParams []string
deviceParams = append(deviceParams, string(object.Driver))
deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", object.DeviceID))
switch object.Type {
case MemoryBackendFile:
objectParams = append(objectParams, string(object.Type))
objectParams = append(objectParams, fmt.Sprintf(",id=%s", object.ID))
objectParams = append(objectParams, fmt.Sprintf(",mem-path=%s", object.MemPath))
objectParams = append(objectParams, fmt.Sprintf(",size=%d", object.Size))
deviceParams = append(deviceParams, fmt.Sprintf(",memdev=%s", object.ID))
}
qemuParams = append(qemuParams, "-device")
qemuParams = append(qemuParams, strings.Join(deviceParams, ""))
qemuParams = append(qemuParams, "-object")
qemuParams = append(qemuParams, strings.Join(objectParams, ""))
return qemuParams
}
// FSDriver represents a qemu filesystem driver.
type FSDriver string
@ -205,8 +175,11 @@ const (
// FSDevice represents a qemu filesystem configuration.
type FSDevice struct {
// Driver is the filesystem driver backend.
Driver FSDriver
// Driver is the qemu device driver
Driver DeviceDriver
// FSDriver is the filesystem driver backend.
FSDriver FSDriver
// ID is the filesystem identifier.
ID string
@ -230,6 +203,30 @@ func (fsdev FSDevice) Valid() bool {
return true
}
// QemuParams returns the qemu parameters built out of this filesystem device.
func (fsdev FSDevice) QemuParams() []string {
var fsParams []string
var deviceParams []string
var qemuParams []string
deviceParams = append(deviceParams, fmt.Sprintf("%s", fsdev.Driver))
deviceParams = append(deviceParams, fmt.Sprintf(",fsdev=%s", fsdev.ID))
deviceParams = append(deviceParams, fmt.Sprintf(",mount_tag=%s", fsdev.MountTag))
fsParams = append(fsParams, string(fsdev.FSDriver))
fsParams = append(fsParams, fmt.Sprintf(",id=%s", fsdev.ID))
fsParams = append(fsParams, fmt.Sprintf(",path=%s", fsdev.Path))
fsParams = append(fsParams, fmt.Sprintf(",security-model=%s", fsdev.SecurityModel))
qemuParams = append(qemuParams, "-device")
qemuParams = append(qemuParams, strings.Join(deviceParams, ""))
qemuParams = append(qemuParams, "-fsdev")
qemuParams = append(qemuParams, strings.Join(fsParams, ""))
return qemuParams
}
// CharDeviceBackend is the character device backend for qemu
type CharDeviceBackend string
@ -257,6 +254,12 @@ const (
type CharDevice struct {
Backend CharDeviceBackend
// Driver is the qemu device driver
Driver DeviceDriver
// DeviceID is the user defined device ID.
DeviceID string
ID string
Path string
}
@ -270,6 +273,46 @@ func (cdev CharDevice) Valid() bool {
return true
}
func appendCharDevice(params []string, cdev CharDevice) ([]string, error) {
if cdev.Valid() == false {
return nil, fmt.Errorf("Invalid character device")
}
var cdevParams []string
cdevParams = append(cdevParams, string(cdev.Backend))
cdevParams = append(cdevParams, fmt.Sprintf(",id=%s", cdev.ID))
cdevParams = append(cdevParams, fmt.Sprintf(",path=%s", cdev.Path))
params = append(params, "-chardev")
params = append(params, strings.Join(cdevParams, ""))
return params, nil
}
// QemuParams returns the qemu parameters built out of this character device.
func (cdev CharDevice) QemuParams() []string {
var cdevParams []string
var deviceParams []string
var qemuParams []string
deviceParams = append(deviceParams, fmt.Sprintf("%s", cdev.Driver))
deviceParams = append(deviceParams, fmt.Sprintf(",chardev=%s", cdev.ID))
deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", cdev.DeviceID))
cdevParams = append(cdevParams, string(cdev.Backend))
cdevParams = append(cdevParams, fmt.Sprintf(",id=%s", cdev.ID))
cdevParams = append(cdevParams, fmt.Sprintf(",path=%s", cdev.Path))
qemuParams = append(qemuParams, "-device")
qemuParams = append(qemuParams, strings.Join(deviceParams, ""))
qemuParams = append(qemuParams, "-chardev")
qemuParams = append(qemuParams, strings.Join(cdevParams, ""))
return qemuParams
}
// NetDeviceType is a qemu networing device type.
type NetDeviceType string
@ -283,6 +326,9 @@ type NetDevice struct {
// Type is the netdev type (e.g. tap).
Type NetDeviceType
// Driver is the qemu device driver
Driver DeviceDriver
// ID is the netdevice identifier.
ID string
@ -320,15 +366,83 @@ func (netdev NetDevice) Valid() bool {
}
}
// QemuParams returns the qemu parameters built out of this network device.
func (netdev NetDevice) QemuParams() []string {
var netdevParams []string
var deviceParams []string
var qemuParams []string
deviceParams = append(deviceParams, fmt.Sprintf("%s", netdev.Driver))
deviceParams = append(deviceParams, fmt.Sprintf(",netdev=%s", netdev.ID))
deviceParams = append(deviceParams, fmt.Sprintf(",mac=%s", netdev.MACAddress))
netdevParams = append(netdevParams, string(netdev.Type))
netdevParams = append(netdevParams, fmt.Sprintf(",id=%s", netdev.ID))
netdevParams = append(netdevParams, fmt.Sprintf(",ifname=%s", netdev.IFName))
if netdev.DownScript != "" {
netdevParams = append(netdevParams, fmt.Sprintf(",downscript=%s", netdev.DownScript))
}
if netdev.Script != "" {
netdevParams = append(netdevParams, fmt.Sprintf(",script=%s", netdev.Script))
}
if len(netdev.FDs) > 0 {
var fdParams []string
for _, fd := range netdev.FDs {
fdParams = append(fdParams, fmt.Sprintf("%d", fd))
}
netdevParams = append(netdevParams, fmt.Sprintf(",fds=%s", strings.Join(fdParams, ":")))
}
if netdev.VHost == true {
netdevParams = append(netdevParams, ",vhost=on")
}
qemuParams = append(qemuParams, "-device")
qemuParams = append(qemuParams, strings.Join(deviceParams, ""))
qemuParams = append(qemuParams, "-netdev")
qemuParams = append(qemuParams, strings.Join(netdevParams, ""))
return qemuParams
}
// SerialDevice represents a qemu serial device.
type SerialDevice struct {
// Driver is the qemu device driver
Driver DeviceDriver
// ID is the serial device identifier.
ID string
}
// Valid returns true if the SerialDevice structure is valid and complete.
func (dev SerialDevice) Valid() bool {
if dev.Driver == "" || dev.ID == "" {
return false
}
return true
}
// QemuParams returns the qemu parameters built out of this serial device.
func (dev SerialDevice) QemuParams() []string {
var deviceParams []string
var qemuParams []string
deviceParams = append(deviceParams, fmt.Sprintf("%s", dev.Driver))
deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", dev.ID))
qemuParams = append(qemuParams, "-device")
qemuParams = append(qemuParams, strings.Join(deviceParams, ""))
return qemuParams
}
// RTCBaseType is the qemu RTC base time type.
type RTCBaseType string
@ -420,6 +534,10 @@ func (qmp QMPSocket) Valid() bool {
return false
}
if qmp.Type != Unix {
return false
}
return true
}
@ -498,8 +616,8 @@ type Config struct {
// QMPSocket is the QMP socket description.
QMPSocket QMPSocket
// Drivers is a list of device drivers for qemu to use.
Drivers []Driver
// Devices is a list of devices for qemu to create and drive.
Devices []Device
// RTC is the qemu Real Time Clock configuration
RTC RTC
@ -583,156 +701,13 @@ func appendQMPSocket(params []string, config Config) []string {
return params
}
func appendObject(params []string, object Object) ([]string, error) {
if object.Valid() == false {
return nil, fmt.Errorf("Invalid Object")
}
var objectParams []string
objectParams = append(objectParams, string(object.Type))
switch object.Type {
case MemoryBackendFile:
objectParams = append(objectParams, fmt.Sprintf(",id=%s", object.ID))
objectParams = append(objectParams, fmt.Sprintf(",mem-path=%s", object.MemPath))
objectParams = append(objectParams, fmt.Sprintf(",size=%d", object.Size))
}
params = append(params, "-object")
params = append(params, strings.Join(objectParams, ""))
return params, nil
}
func appendFilesystemDevice(params []string, fsdev FSDevice) ([]string, error) {
if fsdev.Valid() == false {
return nil, fmt.Errorf("Invalid filesystem device")
}
var fsParams []string
fsParams = append(fsParams, string(fsdev.Driver))
fsParams = append(fsParams, fmt.Sprintf(",id=%s", fsdev.ID))
fsParams = append(fsParams, fmt.Sprintf(",path=%s", fsdev.Path))
fsParams = append(fsParams, fmt.Sprintf(",security-model=%s", fsdev.SecurityModel))
params = append(params, "-fsdev")
params = append(params, strings.Join(fsParams, ""))
return params, nil
}
func appendCharDevice(params []string, cdev CharDevice) ([]string, error) {
if cdev.Valid() == false {
return nil, fmt.Errorf("Invalid character device")
}
var cdevParams []string
cdevParams = append(cdevParams, string(cdev.Backend))
cdevParams = append(cdevParams, fmt.Sprintf(",id=%s", cdev.ID))
cdevParams = append(cdevParams, fmt.Sprintf(",path=%s", cdev.Path))
params = append(params, "-chardev")
params = append(params, strings.Join(cdevParams, ""))
return params, nil
}
func appendNetDevice(params []string, netdev NetDevice) ([]string, error) {
if netdev.Valid() == false {
return nil, fmt.Errorf("Invalid network device")
}
var netdevParams []string
netdevParams = append(netdevParams, string(netdev.Type))
netdevParams = append(netdevParams, fmt.Sprintf(",id=%s", netdev.ID))
netdevParams = append(netdevParams, fmt.Sprintf(",ifname=%s", netdev.IFName))
if netdev.DownScript != "" {
netdevParams = append(netdevParams, fmt.Sprintf(",downscript=%s", netdev.DownScript))
}
if netdev.Script != "" {
netdevParams = append(netdevParams, fmt.Sprintf(",script=%s", netdev.Script))
}
if len(netdev.FDs) > 0 {
var fdParams []string
for _, fd := range netdev.FDs {
fdParams = append(fdParams, fmt.Sprintf("%d", fd))
}
netdevParams = append(netdevParams, fmt.Sprintf(",fds=%s", strings.Join(fdParams, ":")))
}
if netdev.VHost == true {
netdevParams = append(netdevParams, ",vhost=on")
}
params = append(params, "-netdev")
params = append(params, strings.Join(netdevParams, ""))
return params, nil
}
func appendDrivers(params []string, config Config) []string {
for _, d := range config.Drivers {
func appendDevices(params []string, config Config) []string {
for _, d := range config.Devices {
if d.Valid() == false {
continue
}
var driverParams []string
var err error
driverParams = append(driverParams, fmt.Sprintf("%s", d.Driver))
switch device := d.Device.(type) {
case Object:
params, err = appendObject(params, device)
if err != nil {
continue
}
driverParams = append(driverParams, fmt.Sprintf(",id=%s", d.ID))
driverParams = append(driverParams, fmt.Sprintf(",memdev=%s", device.ID))
case FSDevice:
params, err = appendFilesystemDevice(params, device)
if err != nil {
continue
}
driverParams = append(driverParams, fmt.Sprintf(",fsdev=%s", device.ID))
driverParams = append(driverParams, fmt.Sprintf(",mount_tag=%s", device.MountTag))
case SerialDevice:
driverParams = append(driverParams, fmt.Sprintf(",id=%s", d.ID))
case CharDevice:
params, err = appendCharDevice(params, device)
if err != nil {
continue
}
driverParams = append(driverParams, fmt.Sprintf(",chardev=%s", device.ID))
driverParams = append(driverParams, fmt.Sprintf(",id=%s", d.ID))
case NetDevice:
params, err = appendNetDevice(params, device)
if err != nil {
continue
}
driverParams = append(driverParams, fmt.Sprintf(",netdev=%s", device.ID))
driverParams = append(driverParams, fmt.Sprintf(",mac=%s", device.MACAddress))
}
params = append(params, "-device")
params = append(params, strings.Join(driverParams, ""))
params = append(params, d.QemuParams()...)
}
return params
@ -883,7 +858,7 @@ func LaunchQemu(config Config, logger QMPLog) (string, error) {
params = appendQMPSocket(params, config)
params = appendMemory(params, config)
params = appendCPUs(params, config)
params = appendDrivers(params, config)
params = appendDevices(params, config)
params = appendRTC(params, config)
params = appendGlobalParam(params, config)
params = appendVGA(params, config)

View File

@ -34,12 +34,12 @@ func testAppend(structure interface{}, expected string, t *testing.T) {
params = appendMachine([]string{}, config)
case Driver:
case Device:
config := Config{
Drivers: []Driver{s},
Devices: []Device{s},
}
params = appendDrivers([]string{}, config)
params = appendDevices([]string{}, config)
case Knobs:
config := Config{
@ -107,48 +107,41 @@ func TestAppendEmptyMachine(t *testing.T) {
testAppend(machine, "", t)
}
var deviceNVDIMMString = "-object memory-backend-file,id=mem0,mem-path=/root,size=65536 -device nvdimm,id=nv0,memdev=mem0"
var deviceNVDIMMString = "-device nvdimm,id=nv0,memdev=mem0 -object memory-backend-file,id=mem0,mem-path=/root,size=65536"
func TestAppendDeviceNVDIMM(t *testing.T) {
object := Object{
Type: MemoryBackendFile,
ID: "mem0",
MemPath: "/root",
Size: 1 << 16,
Driver: NVDIMM,
Type: MemoryBackendFile,
DeviceID: "nv0",
ID: "mem0",
MemPath: "/root",
Size: 1 << 16,
}
driver := Driver{
Driver: NVDIMM,
ID: "nv0",
Device: object,
}
testAppend(driver, deviceNVDIMMString, t)
testAppend(object, deviceNVDIMMString, t)
}
var deviceFSString = "-fsdev local,id=workload9p,path=/var/lib/docker/devicemapper/mnt/e31ebda2,security-model=none -device virtio-9p-pci,fsdev=workload9p,mount_tag=rootfs"
var deviceFSString = "-device virtio-9p-pci,fsdev=workload9p,mount_tag=rootfs -fsdev local,id=workload9p,path=/var/lib/docker/devicemapper/mnt/e31ebda2,security-model=none"
func TestAppendDeviceFS(t *testing.T) {
fsdev := FSDevice{
Driver: Local,
Driver: Virtio9P,
FSDriver: Local,
ID: "workload9p",
Path: "/var/lib/docker/devicemapper/mnt/e31ebda2",
MountTag: "rootfs",
SecurityModel: None,
}
driver := Driver{
Driver: Virtio9P,
Device: fsdev,
}
testAppend(driver, deviceFSString, t)
testAppend(fsdev, deviceFSString, t)
}
var deviceNetworkString = "-netdev tap,id=tap0,ifname=ceth0,downscript=no,script=no,fds=8:9:10,vhost=on -device virtio-net,netdev=tap0,mac=01:02:de:ad:be:ef"
var deviceNetworkString = "-device virtio-net,netdev=tap0,mac=01:02:de:ad:be:ef -netdev tap,id=tap0,ifname=ceth0,downscript=no,script=no,fds=8:9:10,vhost=on"
func TestAppendDeviceNetwork(t *testing.T) {
netdev := NetDevice{
Driver: VirtioNet,
Type: TAP,
ID: "tap0",
IFName: "ceth0",
@ -159,30 +152,24 @@ func TestAppendDeviceNetwork(t *testing.T) {
MACAddress: "01:02:de:ad:be:ef",
}
driver := Driver{
Driver: VirtioNet,
Device: netdev,
}
testAppend(driver, deviceNetworkString, t)
testAppend(netdev, deviceNetworkString, t)
}
var deviceSerialString = "-device virtio-serial-pci,id=serial0"
func TestAppendDeviceSerial(t *testing.T) {
driver := Driver{
sdev := SerialDevice{
Driver: VirtioSerial,
ID: "serial0",
Device: SerialDevice{},
}
testAppend(driver, deviceSerialString, t)
testAppend(sdev, deviceSerialString, t)
}
func TestAppendEmptyDevice(t *testing.T) {
driver := Driver{}
device := SerialDevice{}
testAppend(driver, "", t)
testAppend(device, "", t)
}
var knobsString = "-no-user-config -nodefaults -nographic"