Files
kata-containers/virtcontainers/qemu_arch_base_test.go
Alice Frosi e3f92fe59b virtcontainer: add error return code
Add error return code to append functions.

Fixes: #2035

Signed-off-by: Alice Frosi <afrosi@de.ibm.com>
2019-09-05 15:28:46 +02:00

565 lines
13 KiB
Go

// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"fmt"
"io/ioutil"
"net"
"path/filepath"
"testing"
govmmQemu "github.com/intel/govmm/qemu"
"github.com/stretchr/testify/assert"
"github.com/kata-containers/runtime/virtcontainers/device/config"
"github.com/kata-containers/runtime/virtcontainers/store"
"github.com/kata-containers/runtime/virtcontainers/types"
"github.com/pkg/errors"
)
const (
qemuArchBaseMachineType = "pc"
qemuArchBaseQemuPath = "/usr/bin/qemu-system-x86_64"
)
var qemuArchBaseQemuPaths = map[string]string{
qemuArchBaseMachineType: qemuArchBaseQemuPath,
}
var qemuArchBaseKernelParamsNonDebug = []Param{
{"quiet", ""},
{"systemd.show_status", "false"},
}
var qemuArchBaseKernelParamsDebug = []Param{
{"debug", ""},
{"systemd.show_status", "true"},
{"systemd.log_level", "debug"},
}
var qemuArchBaseKernelParams = []Param{
{"root", "/dev/vda"},
{"rootfstype", "ext4"},
}
var qemuArchBaseSupportedQemuMachines = []govmmQemu.Machine{
{
Type: qemuArchBaseMachineType,
},
}
func newQemuArchBase() *qemuArchBase {
return &qemuArchBase{
machineType: qemuArchBaseMachineType,
nestedRun: false,
qemuPaths: qemuArchBaseQemuPaths,
supportedQemuMachines: qemuArchBaseSupportedQemuMachines,
kernelParamsNonDebug: qemuArchBaseKernelParamsNonDebug,
kernelParamsDebug: qemuArchBaseKernelParamsDebug,
kernelParams: qemuArchBaseKernelParams,
}
}
func TestQemuArchBaseEnableNestingChecks(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
qemuArchBase.enableNestingChecks()
assert.True(qemuArchBase.nestedRun)
}
func TestQemuArchBaseDisableNestingChecks(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
qemuArchBase.disableNestingChecks()
assert.False(qemuArchBase.nestedRun)
}
func TestQemuArchBaseMachine(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
m, err := qemuArchBase.machine()
assert.NoError(err)
assert.Equal(m.Type, qemuArchBaseMachineType)
machines := []govmmQemu.Machine{
{
Type: "bad",
},
}
qemuArchBase.supportedQemuMachines = machines
m, err = qemuArchBase.machine()
assert.Error(err)
assert.Equal("", m.Type)
}
func TestQemuArchBaseQemuPath(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
p, err := qemuArchBase.qemuPath()
assert.NoError(err)
assert.Equal(p, qemuArchBaseQemuPath)
paths := map[string]string{
"bad": qemuArchBaseQemuPath,
}
qemuArchBase.qemuPaths = paths
p, err = qemuArchBase.qemuPath()
assert.Error(err)
assert.Equal("", p)
}
func TestQemuArchBaseKernelParameters(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
// with debug params
expectedParams := qemuArchBaseKernelParams
debugParams := qemuArchBaseKernelParamsDebug
expectedParams = append(expectedParams, debugParams...)
p := qemuArchBase.kernelParameters(true)
assert.Equal(expectedParams, p)
// with non-debug params
expectedParams = qemuArchBaseKernelParams
nonDebugParams := qemuArchBaseKernelParamsNonDebug
expectedParams = append(expectedParams, nonDebugParams...)
p = qemuArchBase.kernelParameters(false)
assert.Equal(expectedParams, p)
}
func TestQemuArchBaseCapabilities(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
c := qemuArchBase.capabilities()
assert.True(c.IsBlockDeviceHotplugSupported())
}
func TestQemuArchBaseBridges(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
len := 5
qemuArchBase.bridges(uint32(len))
bridges := qemuArchBase.getBridges()
assert.Len(bridges, len)
for i, b := range bridges {
id := fmt.Sprintf("%s-bridge-%d", types.PCI, i)
assert.Equal(types.PCI, b.Type)
assert.Equal(id, b.ID)
assert.NotNil(b.Devices)
}
}
func TestQemuAddDeviceToBridge(t *testing.T) {
assert := assert.New(t)
// addDeviceToBridge successfully
q := newQemuArchBase()
q.machineType = QemuPC
q.bridges(1)
for i := uint32(1); i <= types.PCIBridgeMaxCapacity; i++ {
_, _, err := q.addDeviceToBridge(fmt.Sprintf("qemu-bridge-%d", i), types.PCI)
assert.Nil(err)
}
// fail to add device to bridge cause no more available bridge slot
_, _, err := q.addDeviceToBridge("qemu-bridge-31", types.PCI)
exceptErr := errors.New("no more bridge slots available")
assert.Equal(exceptErr.Error(), err.Error())
// addDeviceToBridge fails cause q.Bridges == 0
q = newQemuArchBase()
q.machineType = QemuPCLite
q.bridges(0)
_, _, err = q.addDeviceToBridge("qemu-bridge", types.PCI)
if assert.Error(err) {
exceptErr = errors.New("failed to get available address from bridges")
assert.Equal(exceptErr.Error(), err.Error())
}
}
func TestQemuArchBaseCPUTopology(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
vcpus := uint32(2)
expectedSMP := govmmQemu.SMP{
CPUs: vcpus,
Sockets: defaultMaxQemuVCPUs,
Cores: defaultCores,
Threads: defaultThreads,
MaxCPUs: defaultMaxQemuVCPUs,
}
smp := qemuArchBase.cpuTopology(vcpus, defaultMaxQemuVCPUs)
assert.Equal(expectedSMP, smp)
}
func TestQemuArchBaseCPUModel(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
assert.Equal(defaultCPUModel, qemuArchBase.cpuModel())
}
func TestQemuArchBaseMemoryTopology(t *testing.T) {
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
hostMem := uint64(100)
mem := uint64(120)
slots := uint8(12)
expectedMemory := govmmQemu.Memory{
Size: fmt.Sprintf("%dM", mem),
Slots: slots,
MaxMem: fmt.Sprintf("%dM", hostMem),
}
m := qemuArchBase.memoryTopology(mem, hostMem, slots)
assert.Equal(expectedMemory, m)
}
func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govmmQemu.Device) {
var devices []govmmQemu.Device
var err error
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
switch s := structure.(type) {
case types.Volume:
devices, err = qemuArchBase.append9PVolume(devices, s)
case types.Socket:
devices = qemuArchBase.appendSocket(devices, s)
case config.BlockDrive:
devices, err = qemuArchBase.appendBlockDevice(devices, s)
case config.VFIODev:
devices = qemuArchBase.appendVFIODevice(devices, s)
case config.VhostUserDeviceAttrs:
devices, err = qemuArchBase.appendVhostUserDevice(devices, s)
}
assert.NoError(err)
assert.Equal(devices, expected)
}
func TestQemuArchBaseAppendConsoles(t *testing.T) {
var devices []govmmQemu.Device
var err error
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
path := filepath.Join(store.SandboxRuntimeRootPath(sandboxID), consoleSocket)
expectedOut := []govmmQemu.Device{
govmmQemu.SerialDevice{
Driver: govmmQemu.VirtioSerial,
ID: "serial0",
},
govmmQemu.CharDevice{
Driver: govmmQemu.Console,
Backend: govmmQemu.Socket,
DeviceID: "console0",
ID: "charconsole0",
Path: path,
},
}
devices, err = qemuArchBase.appendConsole(devices, path)
assert.NoError(err)
assert.Equal(expectedOut, devices)
}
func TestQemuArchBaseAppendImage(t *testing.T) {
var devices []govmmQemu.Device
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
image, err := ioutil.TempFile("", "img")
assert.NoError(err)
err = image.Close()
assert.NoError(err)
devices, err = qemuArchBase.appendImage(devices, image.Name())
assert.NoError(err)
assert.Len(devices, 1)
drive, ok := devices[0].(govmmQemu.BlockDevice)
assert.True(ok)
expectedOut := []govmmQemu.Device{
govmmQemu.BlockDevice{
Driver: govmmQemu.VirtioBlock,
ID: drive.ID,
File: image.Name(),
AIO: govmmQemu.Threads,
Format: "raw",
Interface: "none",
},
}
assert.Equal(expectedOut, devices)
}
func TestQemuArchBaseAppendBridges(t *testing.T) {
var devices []govmmQemu.Device
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
qemuArchBase.bridges(1)
bridges := qemuArchBase.getBridges()
assert.Len(bridges, 1)
devices = qemuArchBase.appendBridges(devices)
assert.Len(devices, 1)
expectedOut := []govmmQemu.Device{
govmmQemu.BridgeDevice{
Type: govmmQemu.PCIBridge,
Bus: defaultBridgeBus,
ID: bridges[0].ID,
Chassis: 1,
SHPC: true,
Addr: "2",
},
}
assert.Equal(expectedOut, devices)
}
func TestQemuArchBaseAppend9PVolume(t *testing.T) {
mountTag := "testMountTag"
hostPath := "testHostPath"
expectedOut := []govmmQemu.Device{
govmmQemu.FSDevice{
Driver: govmmQemu.Virtio9P,
FSDriver: govmmQemu.Local,
ID: fmt.Sprintf("extra-9p-%s", mountTag),
Path: hostPath,
MountTag: mountTag,
SecurityModel: govmmQemu.None,
},
}
volume := types.Volume{
MountTag: mountTag,
HostPath: hostPath,
}
testQemuArchBaseAppend(t, volume, expectedOut)
}
func TestQemuArchBaseAppendSocket(t *testing.T) {
deviceID := "channelTest"
id := "charchTest"
hostPath := "/tmp/hyper_test.sock"
name := "sh.hyper.channel.test"
expectedOut := []govmmQemu.Device{
govmmQemu.CharDevice{
Driver: govmmQemu.VirtioSerialPort,
Backend: govmmQemu.Socket,
DeviceID: deviceID,
ID: id,
Path: hostPath,
Name: name,
},
}
socket := types.Socket{
DeviceID: deviceID,
ID: id,
HostPath: hostPath,
Name: name,
}
testQemuArchBaseAppend(t, socket, expectedOut)
}
func TestQemuArchBaseAppendBlockDevice(t *testing.T) {
id := "blockDevTest"
file := "/root"
format := "raw"
expectedOut := []govmmQemu.Device{
govmmQemu.BlockDevice{
Driver: govmmQemu.VirtioBlock,
ID: id,
File: "/root",
AIO: govmmQemu.Threads,
Format: govmmQemu.BlockDeviceFormat(format),
Interface: "none",
},
}
drive := config.BlockDrive{
File: file,
Format: format,
ID: id,
}
testQemuArchBaseAppend(t, drive, expectedOut)
}
func TestQemuArchBaseAppendVhostUserDevice(t *testing.T) {
socketPath := "nonexistentpath.sock"
macAddress := "00:11:22:33:44:55:66"
id := "deadbeef"
expectedOut := []govmmQemu.Device{
govmmQemu.VhostUserDevice{
SocketPath: socketPath,
CharDevID: fmt.Sprintf("char-%s", id),
TypeDevID: fmt.Sprintf("net-%s", id),
Address: macAddress,
VhostUserType: config.VhostUserNet,
},
}
vhostUserDevice := config.VhostUserDeviceAttrs{
Type: config.VhostUserNet,
MacAddress: macAddress,
}
vhostUserDevice.DevID = id
vhostUserDevice.SocketPath = socketPath
testQemuArchBaseAppend(t, vhostUserDevice, expectedOut)
}
func TestQemuArchBaseAppendVFIODevice(t *testing.T) {
bdf := "02:10.1"
expectedOut := []govmmQemu.Device{
govmmQemu.VFIODevice{
BDF: bdf,
},
}
vfDevice := config.VFIODev{
BDF: bdf,
}
testQemuArchBaseAppend(t, vfDevice, expectedOut)
}
func TestQemuArchBaseAppendVFIODeviceWithVendorDeviceID(t *testing.T) {
bdf := "02:10.1"
vendorID := "0x1234"
deviceID := "0x5678"
expectedOut := []govmmQemu.Device{
govmmQemu.VFIODevice{
BDF: bdf,
VendorID: vendorID,
DeviceID: deviceID,
},
}
vfDevice := config.VFIODev{
BDF: bdf,
VendorID: vendorID,
DeviceID: deviceID,
}
testQemuArchBaseAppend(t, vfDevice, expectedOut)
}
func TestQemuArchBaseAppendSCSIController(t *testing.T) {
var devices []govmmQemu.Device
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
expectedOut := []govmmQemu.Device{
govmmQemu.SCSIController{
ID: scsiControllerID,
},
}
devices, ioThread, err := qemuArchBase.appendSCSIController(devices, false)
assert.Equal(expectedOut, devices)
assert.Nil(ioThread)
assert.NoError(err)
_, ioThread, err = qemuArchBase.appendSCSIController(devices, true)
assert.NotNil(ioThread)
assert.NoError(err)
}
func TestQemuArchBaseAppendNetwork(t *testing.T) {
var devices []govmmQemu.Device
var err error
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
macAddr := net.HardwareAddr{0x02, 0x00, 0xCA, 0xFE, 0x00, 0x04}
macvlanEp := &BridgedMacvlanEndpoint{
NetPair: NetworkInterfacePair{
TapInterface: TapInterface{
ID: "uniqueTestID-4",
Name: "br4_kata",
TAPIface: NetworkInterface{
Name: "tap4_kata",
},
},
VirtIface: NetworkInterface{
Name: "eth4",
HardAddr: macAddr.String(),
},
NetInterworkingModel: DefaultNetInterworkingModel,
},
EndpointType: BridgedMacvlanEndpointType,
}
macvtapEp := &MacvtapEndpoint{
EndpointType: MacvtapEndpointType,
EndpointProperties: NetworkInfo{
Iface: NetlinkIface{
Type: "macvtap",
},
},
}
expectedOut := []govmmQemu.Device{
govmmQemu.NetDevice{
Type: networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel),
Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", 0),
IFName: macvlanEp.NetPair.TAPIface.Name,
MACAddress: macvlanEp.NetPair.TAPIface.HardAddr,
DownScript: "no",
Script: "no",
FDs: macvlanEp.NetPair.VMFds,
VhostFDs: macvlanEp.NetPair.VhostFds,
},
govmmQemu.NetDevice{
Type: govmmQemu.MACVTAP,
Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", 1),
IFName: macvtapEp.Name(),
MACAddress: macvtapEp.HardwareAddr(),
DownScript: "no",
Script: "no",
FDs: macvtapEp.VMFds,
VhostFDs: macvtapEp.VhostFds,
},
}
devices, err = qemuArchBase.appendNetwork(devices, macvlanEp)
assert.NoError(err)
devices, err = qemuArchBase.appendNetwork(devices, macvtapEp)
assert.NoError(err)
assert.Equal(expectedOut, devices)
}