mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-18 06:44:23 +01:00
runtime: move all code to src/runtime
To prepare for merging into kata-containers repository. Signed-off-by: Peng Tao <bergwolf@hyper.sh>
This commit is contained in:
603
src/runtime/virtcontainers/qemu_test.go
Normal file
603
src/runtime/virtcontainers/qemu_test.go
Normal file
@@ -0,0 +1,603 @@
|
||||
// Copyright (c) 2016 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package virtcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
govmmQemu "github.com/intel/govmm/qemu"
|
||||
"github.com/kata-containers/runtime/virtcontainers/device/config"
|
||||
"github.com/kata-containers/runtime/virtcontainers/persist"
|
||||
"github.com/kata-containers/runtime/virtcontainers/types"
|
||||
"github.com/kata-containers/runtime/virtcontainers/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func newQemuConfig() HypervisorConfig {
|
||||
return HypervisorConfig{
|
||||
KernelPath: testQemuKernelPath,
|
||||
ImagePath: testQemuImagePath,
|
||||
InitrdPath: testQemuInitrdPath,
|
||||
HypervisorPath: testQemuPath,
|
||||
NumVCPUs: defaultVCPUs,
|
||||
MemorySize: defaultMemSzMiB,
|
||||
DefaultBridges: defaultBridges,
|
||||
BlockDeviceDriver: defaultBlockDriver,
|
||||
DefaultMaxVCPUs: defaultMaxQemuVCPUs,
|
||||
Msize9p: defaultMsize9p,
|
||||
}
|
||||
}
|
||||
|
||||
func testQemuKernelParameters(t *testing.T, kernelParams []Param, expected string, debug bool) {
|
||||
qemuConfig := newQemuConfig()
|
||||
qemuConfig.KernelParams = kernelParams
|
||||
assert := assert.New(t)
|
||||
|
||||
if debug == true {
|
||||
qemuConfig.Debug = true
|
||||
}
|
||||
|
||||
q := &qemu{
|
||||
config: qemuConfig,
|
||||
arch: &qemuArchBase{},
|
||||
}
|
||||
|
||||
params := q.kernelParameters()
|
||||
assert.Equal(params, expected)
|
||||
}
|
||||
|
||||
func TestQemuKernelParameters(t *testing.T) {
|
||||
expectedOut := fmt.Sprintf("panic=1 nr_cpus=%d agent.use_vsock=false foo=foo bar=bar", MaxQemuVCPUs())
|
||||
params := []Param{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
}
|
||||
|
||||
testQemuKernelParameters(t, params, expectedOut, true)
|
||||
testQemuKernelParameters(t, params, expectedOut, false)
|
||||
}
|
||||
|
||||
func TestQemuCreateSandbox(t *testing.T) {
|
||||
qemuConfig := newQemuConfig()
|
||||
assert := assert.New(t)
|
||||
|
||||
store, err := persist.GetDriver()
|
||||
assert.NoError(err)
|
||||
q := &qemu{
|
||||
store: store,
|
||||
}
|
||||
sandbox := &Sandbox{
|
||||
ctx: context.Background(),
|
||||
id: "testSandbox",
|
||||
config: &SandboxConfig{
|
||||
HypervisorConfig: qemuConfig,
|
||||
},
|
||||
}
|
||||
|
||||
// Create the hypervisor fake binary
|
||||
testQemuPath := filepath.Join(testDir, testHypervisor)
|
||||
_, err = os.Create(testQemuPath)
|
||||
assert.NoError(err)
|
||||
|
||||
// Create parent dir path for hypervisor.json
|
||||
parentDir := filepath.Join(q.store.RunStoragePath(), sandbox.id)
|
||||
assert.NoError(os.MkdirAll(parentDir, DirMode))
|
||||
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
assert.NoError(err)
|
||||
assert.NoError(os.RemoveAll(parentDir))
|
||||
assert.Exactly(qemuConfig, q.config)
|
||||
}
|
||||
|
||||
func TestQemuCreateSandboxMissingParentDirFail(t *testing.T) {
|
||||
qemuConfig := newQemuConfig()
|
||||
assert := assert.New(t)
|
||||
|
||||
store, err := persist.GetDriver()
|
||||
assert.NoError(err)
|
||||
q := &qemu{
|
||||
store: store,
|
||||
}
|
||||
sandbox := &Sandbox{
|
||||
ctx: context.Background(),
|
||||
id: "testSandbox",
|
||||
config: &SandboxConfig{
|
||||
HypervisorConfig: qemuConfig,
|
||||
},
|
||||
}
|
||||
|
||||
// Create the hypervisor fake binary
|
||||
testQemuPath := filepath.Join(testDir, testHypervisor)
|
||||
_, err = os.Create(testQemuPath)
|
||||
assert.NoError(err)
|
||||
|
||||
// Ensure parent dir path for hypervisor.json does not exist.
|
||||
parentDir := filepath.Join(q.store.RunStoragePath(), sandbox.id)
|
||||
assert.NoError(os.RemoveAll(parentDir))
|
||||
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestQemuCPUTopology(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
vcpus := 1
|
||||
|
||||
q := &qemu{
|
||||
arch: &qemuArchBase{},
|
||||
config: HypervisorConfig{
|
||||
NumVCPUs: uint32(vcpus),
|
||||
DefaultMaxVCPUs: uint32(vcpus),
|
||||
},
|
||||
}
|
||||
|
||||
expectedOut := govmmQemu.SMP{
|
||||
CPUs: uint32(vcpus),
|
||||
Sockets: uint32(vcpus),
|
||||
Cores: defaultCores,
|
||||
Threads: defaultThreads,
|
||||
MaxCPUs: uint32(vcpus),
|
||||
}
|
||||
|
||||
smp := q.cpuTopology()
|
||||
assert.Exactly(smp, expectedOut)
|
||||
}
|
||||
|
||||
func TestQemuMemoryTopology(t *testing.T) {
|
||||
mem := uint32(1000)
|
||||
slots := uint32(8)
|
||||
assert := assert.New(t)
|
||||
|
||||
q := &qemu{
|
||||
arch: &qemuArchBase{},
|
||||
config: HypervisorConfig{
|
||||
MemorySize: mem,
|
||||
MemSlots: slots,
|
||||
},
|
||||
}
|
||||
|
||||
hostMemKb, err := getHostMemorySizeKb(procMemInfo)
|
||||
assert.NoError(err)
|
||||
memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024))
|
||||
|
||||
expectedOut := govmmQemu.Memory{
|
||||
Size: fmt.Sprintf("%dM", mem),
|
||||
Slots: uint8(slots),
|
||||
MaxMem: memMax,
|
||||
}
|
||||
|
||||
memory, err := q.memoryTopology()
|
||||
assert.NoError(err)
|
||||
assert.Exactly(memory, expectedOut)
|
||||
}
|
||||
|
||||
func testQemuAddDevice(t *testing.T, devInfo interface{}, devType deviceType, expected []govmmQemu.Device) {
|
||||
assert := assert.New(t)
|
||||
q := &qemu{
|
||||
ctx: context.Background(),
|
||||
arch: &qemuArchBase{},
|
||||
}
|
||||
|
||||
err := q.addDevice(devInfo, devType)
|
||||
assert.NoError(err)
|
||||
assert.Exactly(q.qemuConfig.Devices, expected)
|
||||
}
|
||||
|
||||
func TestQemuAddDeviceFsDev(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,
|
||||
}
|
||||
|
||||
testQemuAddDevice(t, volume, fsDev, expectedOut)
|
||||
}
|
||||
|
||||
func TestQemuAddDeviceVhostUserBlk(t *testing.T) {
|
||||
socketPath := "/test/socket/path"
|
||||
devID := "testDevID"
|
||||
|
||||
expectedOut := []govmmQemu.Device{
|
||||
govmmQemu.VhostUserDevice{
|
||||
SocketPath: socketPath,
|
||||
CharDevID: utils.MakeNameID("char", devID, maxDevIDSize),
|
||||
VhostUserType: govmmQemu.VhostUserBlk,
|
||||
},
|
||||
}
|
||||
|
||||
vDevice := config.VhostUserDeviceAttrs{
|
||||
DevID: devID,
|
||||
SocketPath: socketPath,
|
||||
Type: config.VhostUserBlk,
|
||||
}
|
||||
|
||||
testQemuAddDevice(t, vDevice, vhostuserDev, expectedOut)
|
||||
}
|
||||
|
||||
func TestQemuAddDeviceSerialPortDev(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,
|
||||
}
|
||||
|
||||
testQemuAddDevice(t, socket, serialPortDev, expectedOut)
|
||||
}
|
||||
|
||||
func TestQemuAddDeviceKataVSOCK(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
assert.NoError(err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
vsockFilename := filepath.Join(dir, "vsock")
|
||||
|
||||
contextID := uint64(3)
|
||||
port := uint32(1024)
|
||||
|
||||
vsockFile, err := os.Create(vsockFilename)
|
||||
assert.NoError(err)
|
||||
defer vsockFile.Close()
|
||||
|
||||
expectedOut := []govmmQemu.Device{
|
||||
govmmQemu.VSOCKDevice{
|
||||
ID: fmt.Sprintf("vsock-%d", contextID),
|
||||
ContextID: contextID,
|
||||
VHostFD: vsockFile,
|
||||
},
|
||||
}
|
||||
|
||||
vsock := types.VSock{
|
||||
ContextID: contextID,
|
||||
Port: port,
|
||||
VhostFd: vsockFile,
|
||||
}
|
||||
|
||||
testQemuAddDevice(t, vsock, vSockPCIDev, expectedOut)
|
||||
}
|
||||
|
||||
func TestQemuGetSandboxConsole(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
store, err := persist.GetDriver()
|
||||
assert.NoError(err)
|
||||
q := &qemu{
|
||||
ctx: context.Background(),
|
||||
store: store,
|
||||
}
|
||||
sandboxID := "testSandboxID"
|
||||
expected := filepath.Join(q.store.RunVMStoragePath(), sandboxID, consoleSocket)
|
||||
|
||||
result, err := q.getSandboxConsole(sandboxID)
|
||||
assert.NoError(err)
|
||||
assert.Equal(result, expected)
|
||||
}
|
||||
|
||||
func TestQemuCapabilities(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
q := &qemu{
|
||||
ctx: context.Background(),
|
||||
arch: &qemuArchBase{},
|
||||
}
|
||||
|
||||
caps := q.capabilities()
|
||||
assert.True(caps.IsBlockDeviceHotplugSupported())
|
||||
}
|
||||
|
||||
func TestQemuQemuPath(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
f, err := ioutil.TempFile("", "qemu")
|
||||
assert.NoError(err)
|
||||
defer func() { _ = f.Close() }()
|
||||
defer func() { _ = os.Remove(f.Name()) }()
|
||||
|
||||
expectedPath := f.Name()
|
||||
qemuConfig := newQemuConfig()
|
||||
qemuConfig.HypervisorPath = expectedPath
|
||||
qkvm := &qemuArchBase{
|
||||
machineType: "pc",
|
||||
qemuPaths: map[string]string{
|
||||
"pc": expectedPath,
|
||||
},
|
||||
}
|
||||
|
||||
q := &qemu{
|
||||
config: qemuConfig,
|
||||
arch: qkvm,
|
||||
}
|
||||
|
||||
// get config hypervisor path
|
||||
path, err := q.qemuPath()
|
||||
assert.NoError(err)
|
||||
assert.Equal(path, expectedPath)
|
||||
|
||||
// config hypervisor path does not exist
|
||||
q.config.HypervisorPath = "/abc/rgb/123"
|
||||
path, err = q.qemuPath()
|
||||
assert.Error(err)
|
||||
assert.Equal(path, "")
|
||||
|
||||
// get arch hypervisor path
|
||||
q.config.HypervisorPath = ""
|
||||
path, err = q.qemuPath()
|
||||
assert.NoError(err)
|
||||
assert.Equal(path, expectedPath)
|
||||
|
||||
// bad machine type, arch should fail
|
||||
qkvm.machineType = "rgb"
|
||||
q.arch = qkvm
|
||||
path, err = q.qemuPath()
|
||||
assert.Error(err)
|
||||
assert.Equal(path, "")
|
||||
}
|
||||
|
||||
func TestHotplugUnsupportedDeviceType(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
qemuConfig := newQemuConfig()
|
||||
q := &qemu{
|
||||
ctx: context.Background(),
|
||||
id: "qemuTest",
|
||||
config: qemuConfig,
|
||||
}
|
||||
|
||||
_, err := q.hotplugAddDevice(&memoryDevice{0, 128, uint64(0), false}, fsDev)
|
||||
assert.Error(err)
|
||||
_, err = q.hotplugRemoveDevice(&memoryDevice{0, 128, uint64(0), false}, fsDev)
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
func TestQMPSetupShutdown(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
qemuConfig := newQemuConfig()
|
||||
q := &qemu{
|
||||
config: qemuConfig,
|
||||
}
|
||||
|
||||
q.qmpShutdown()
|
||||
|
||||
q.qmpMonitorCh.qmp = &govmmQemu.QMP{}
|
||||
err := q.qmpSetup()
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestQemuCleanup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
q := &qemu{
|
||||
ctx: context.Background(),
|
||||
config: newQemuConfig(),
|
||||
}
|
||||
|
||||
err := q.cleanup()
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestQemuGrpc(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := newQemuConfig()
|
||||
q := &qemu{
|
||||
id: "testqemu",
|
||||
config: config,
|
||||
}
|
||||
|
||||
json, err := q.toGrpc()
|
||||
assert.Nil(err)
|
||||
|
||||
var q2 qemu
|
||||
err = q2.fromGrpc(context.Background(), &config, json)
|
||||
assert.Nil(err)
|
||||
|
||||
assert.True(q.id == q2.id)
|
||||
}
|
||||
|
||||
func TestQemuFileBackedMem(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Check default Filebackedmem location for virtio-fs
|
||||
sandbox, err := createQemuSandboxConfig()
|
||||
assert.NoError(err)
|
||||
|
||||
q := &qemu{
|
||||
store: sandbox.newStore,
|
||||
}
|
||||
sandbox.config.HypervisorConfig.SharedFS = config.VirtioFS
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
assert.NoError(err)
|
||||
|
||||
assert.Equal(q.qemuConfig.Knobs.FileBackedMem, true)
|
||||
assert.Equal(q.qemuConfig.Knobs.MemShared, true)
|
||||
assert.Equal(q.qemuConfig.Memory.Path, fallbackFileBackedMemDir)
|
||||
|
||||
// Check failure for VM templating
|
||||
sandbox, err = createQemuSandboxConfig()
|
||||
assert.NoError(err)
|
||||
|
||||
q = &qemu{
|
||||
store: sandbox.newStore,
|
||||
}
|
||||
sandbox.config.HypervisorConfig.BootToBeTemplate = true
|
||||
sandbox.config.HypervisorConfig.SharedFS = config.VirtioFS
|
||||
sandbox.config.HypervisorConfig.MemoryPath = fallbackFileBackedMemDir
|
||||
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
|
||||
expectErr := errors.New("VM templating has been enabled with either virtio-fs or file backed memory and this configuration will not work")
|
||||
assert.Equal(expectErr.Error(), err.Error())
|
||||
|
||||
// Check Setting of non-existent shared-mem path
|
||||
sandbox, err = createQemuSandboxConfig()
|
||||
assert.NoError(err)
|
||||
|
||||
q = &qemu{
|
||||
store: sandbox.newStore,
|
||||
}
|
||||
sandbox.config.HypervisorConfig.FileBackedMemRootDir = "/tmp/xyzabc"
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
assert.NoError(err)
|
||||
assert.Equal(q.qemuConfig.Knobs.FileBackedMem, false)
|
||||
assert.Equal(q.qemuConfig.Knobs.MemShared, false)
|
||||
assert.Equal(q.qemuConfig.Memory.Path, "")
|
||||
|
||||
// Check setting vhost-user storage with Hugepages
|
||||
sandbox, err = createQemuSandboxConfig()
|
||||
assert.NoError(err)
|
||||
|
||||
q = &qemu{
|
||||
store: sandbox.newStore,
|
||||
}
|
||||
sandbox.config.HypervisorConfig.EnableVhostUserStore = true
|
||||
sandbox.config.HypervisorConfig.HugePages = true
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
assert.NoError(err)
|
||||
assert.Equal(q.qemuConfig.Knobs.MemShared, true)
|
||||
|
||||
// Check failure for vhost-user storage
|
||||
sandbox, err = createQemuSandboxConfig()
|
||||
assert.NoError(err)
|
||||
|
||||
q = &qemu{
|
||||
store: sandbox.newStore,
|
||||
}
|
||||
sandbox.config.HypervisorConfig.EnableVhostUserStore = true
|
||||
sandbox.config.HypervisorConfig.HugePages = false
|
||||
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
|
||||
|
||||
expectErr = errors.New("Vhost-user-blk/scsi is enabled without HugePages. This configuration will not work")
|
||||
assert.Equal(expectErr.Error(), err.Error())
|
||||
}
|
||||
|
||||
func createQemuSandboxConfig() (*Sandbox, error) {
|
||||
|
||||
qemuConfig := newQemuConfig()
|
||||
sandbox := Sandbox{
|
||||
ctx: context.Background(),
|
||||
id: "testSandbox",
|
||||
config: &SandboxConfig{
|
||||
HypervisorConfig: qemuConfig,
|
||||
},
|
||||
}
|
||||
|
||||
newStore, err := persist.GetDriver()
|
||||
if err != nil {
|
||||
return &Sandbox{}, err
|
||||
}
|
||||
sandbox.newStore = newStore
|
||||
|
||||
return &sandbox, nil
|
||||
}
|
||||
|
||||
func TestQemuVirtiofsdArgs(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
q := &qemu{
|
||||
id: "foo",
|
||||
config: HypervisorConfig{
|
||||
VirtioFSCache: "none",
|
||||
Debug: true,
|
||||
},
|
||||
}
|
||||
|
||||
savedKataHostSharedDir := kataHostSharedDir
|
||||
kataHostSharedDir = func() string {
|
||||
return "test-share-dir"
|
||||
}
|
||||
defer func() {
|
||||
kataHostSharedDir = savedKataHostSharedDir
|
||||
}()
|
||||
|
||||
result := "--fd=123 -o source=test-share-dir/foo -o cache=none --syslog -o no_posix_lock -d"
|
||||
args := q.virtiofsdArgs(123)
|
||||
assert.Equal(strings.Join(args, " "), result)
|
||||
|
||||
q.config.Debug = false
|
||||
result = "--fd=123 -o source=test-share-dir/foo -o cache=none --syslog -o no_posix_lock -f"
|
||||
args = q.virtiofsdArgs(123)
|
||||
assert.Equal(strings.Join(args, " "), result)
|
||||
}
|
||||
|
||||
func TestQemuGetpids(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
qemuConfig := newQemuConfig()
|
||||
q := &qemu{}
|
||||
pids := q.getPids()
|
||||
assert.NotNil(pids)
|
||||
assert.True(len(pids) == 1)
|
||||
assert.True(pids[0] == 0)
|
||||
|
||||
q = &qemu{
|
||||
config: qemuConfig,
|
||||
}
|
||||
f, err := ioutil.TempFile("", "qemu-test-")
|
||||
assert.Nil(err)
|
||||
tmpfile := f.Name()
|
||||
f.Close()
|
||||
defer os.Remove(tmpfile)
|
||||
|
||||
q.qemuConfig.PidFile = tmpfile
|
||||
pids = q.getPids()
|
||||
assert.True(len(pids) == 1)
|
||||
assert.True(pids[0] == 0)
|
||||
|
||||
err = ioutil.WriteFile(tmpfile, []byte("100"), 0)
|
||||
assert.Nil(err)
|
||||
pids = q.getPids()
|
||||
assert.True(len(pids) == 1)
|
||||
assert.True(pids[0] == 100)
|
||||
|
||||
q.state.VirtiofsdPid = 200
|
||||
pids = q.getPids()
|
||||
assert.True(len(pids) == 2)
|
||||
assert.True(pids[0] == 100)
|
||||
assert.True(pids[1] == 200)
|
||||
}
|
||||
Reference in New Issue
Block a user