mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-09-14 05:19:21 +00:00
Merge pull request #990 from alicefr/s390x
s390x: add support for s390x
This commit is contained in:
12
arch/s390x-options.mk
Normal file
12
arch/s390x-options.mk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (c) 2018 IBM
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
# s390x settings
|
||||||
|
|
||||||
|
MACHINETYPE := s390-ccw-virtio
|
||||||
|
KERNELPARAMS :=
|
||||||
|
MACHINEACCELERATORS :=
|
||||||
|
|
||||||
|
QEMUCMD := qemu-system-s390x
|
21
cli/kata-check_data_s390x_test.go
Normal file
21
cli/kata-check_data_s390x_test.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2018 IBM
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
const testCPUInfoTemplate = `
|
||||||
|
vendor_id : IBM/S390
|
||||||
|
# processors : 4
|
||||||
|
bogomips per cpu: 20325.00
|
||||||
|
max thread id : 0
|
||||||
|
features : esan3 zarch stfle msa ldisp eimm dfp edat etf3eh highgprs te vx sie
|
||||||
|
cache0 : level=1 type=Data scope=Private size=128K line_size=256 associativity=8
|
||||||
|
cache1 : level=1 type=Instruction scope=Private size=96K line_size=256 associativity=6
|
||||||
|
cache2 : level=2 type=Data scope=Private size=2048K line_size=256 associativity=8
|
||||||
|
cache3 : level=2 type=Instruction scope=Private size=2048K line_size=256 associativity=8
|
||||||
|
cache4 : level=3 type=Unified scope=Shared size=65536K line_size=256 associativity=16
|
||||||
|
cache5 : level=4 type=Unified scope=Shared size=491520K line_size=256 associativity=30
|
||||||
|
processor 0: version = FF, identification = FFFFFF, machine = 2964
|
||||||
|
`
|
124
cli/kata-check_s390x.go
Normal file
124
cli/kata-check_s390x.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (c) 2018 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cpuFlagsTag = genericCPUFlagsTag
|
||||||
|
archCPUVendorField = genericCPUVendorField
|
||||||
|
// On s390x the cpu model is indicated by the field machine.
|
||||||
|
// Example:
|
||||||
|
// processor 0: version = FF, identification = 3FEC87, machine = 2964
|
||||||
|
archCPUModelField = "machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// archRequiredCPUFlags maps a CPU flag value to search for and a
|
||||||
|
// human-readable description of that value.
|
||||||
|
var archRequiredCPUFlags = map[string]string{}
|
||||||
|
|
||||||
|
// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for
|
||||||
|
// and a human-readable description of that value.
|
||||||
|
var archRequiredCPUAttribs = map[string]string{}
|
||||||
|
|
||||||
|
// archRequiredKernelModules maps a required module name to a human-readable
|
||||||
|
// description of the modules functionality and an optional list of
|
||||||
|
// required module parameters.
|
||||||
|
var archRequiredKernelModules = map[string]kernelModule{
|
||||||
|
"kvm": {
|
||||||
|
desc: "Kernel-based Virtual Machine",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCPUtype() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// kvmIsUsable determines if it will be possible to create a full virtual machine
|
||||||
|
// by creating a minimal VM and then deleting it.
|
||||||
|
func kvmIsUsable() error {
|
||||||
|
return genericKvmIsUsable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func archHostCanCreateVMContainer() error {
|
||||||
|
return kvmIsUsable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostIsVMContainerCapable checks to see if the host is theoretically capable
|
||||||
|
// of creating a VM container.
|
||||||
|
func hostIsVMContainerCapable(details vmContainerCapableDetails) error {
|
||||||
|
|
||||||
|
_, err := getCPUInfo(details.cpuInfoFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("ERROR: %s", failMessage)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool {
|
||||||
|
return genericArchKernelParamHandler(onVMM, fields, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getS390xCPUDetails returns the cpu information
|
||||||
|
func getS390xCPUDetails() (vendor, model string, err error) {
|
||||||
|
prefixModel := "processor"
|
||||||
|
cpuinfo, err := getCPUInfo(procCPUInfo)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(cpuinfo, "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if archCPUVendorField != "" {
|
||||||
|
if strings.HasPrefix(line, archCPUVendorField) {
|
||||||
|
fields := strings.Split(line, ":")
|
||||||
|
if len(fields) > 1 {
|
||||||
|
vendor = strings.TrimSpace(fields[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if archCPUModelField != "" {
|
||||||
|
if strings.HasPrefix(line, prefixModel) {
|
||||||
|
fields := strings.Split(strings.TrimSpace(line), ",")
|
||||||
|
cpuModel := strings.Split(fields[2], "=")
|
||||||
|
model = strings.TrimSpace(cpuModel[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vendor == "" {
|
||||||
|
return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if model == "" {
|
||||||
|
return "", "", fmt.Errorf("Error in parsing cpu model from %v", procCPUInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vendor, model, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCPUDetails() (vendor, model string, err error) {
|
||||||
|
if vendor, model, err := genericGetCPUDetails(); err == nil {
|
||||||
|
return vendor, model, nil
|
||||||
|
}
|
||||||
|
return getS390xCPUDetails()
|
||||||
|
}
|
247
cli/kata-check_s390x_test.go
Normal file
247
cli/kata-check_s390x_test.go
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
// Copyright (c) 2018 IBM
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) {
|
||||||
|
createModules(assert, cpuInfoFile, moduleData)
|
||||||
|
|
||||||
|
// all the modules files have now been created, so deal with the
|
||||||
|
// cpuinfo data.
|
||||||
|
for _, d := range cpuData {
|
||||||
|
err := makeCPUInfoFile(cpuInfoFile, d.vendorID, d.flags)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
details := vmContainerCapableDetails{
|
||||||
|
cpuInfoFile: cpuInfoFile,
|
||||||
|
requiredCPUFlags: archRequiredCPUFlags,
|
||||||
|
requiredCPUAttribs: archRequiredCPUAttribs,
|
||||||
|
requiredKernelModules: archRequiredKernelModules,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = hostIsVMContainerCapable(details)
|
||||||
|
if d.expectError {
|
||||||
|
assert.Error(err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCCCheckCLIFunction(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
savedSysModuleDir := sysModuleDir
|
||||||
|
savedProcCPUInfo := procCPUInfo
|
||||||
|
|
||||||
|
cpuInfoFile := filepath.Join(dir, "cpuinfo")
|
||||||
|
|
||||||
|
// XXX: override
|
||||||
|
sysModuleDir = filepath.Join(dir, "sys/module")
|
||||||
|
procCPUInfo = cpuInfoFile
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
sysModuleDir = savedSysModuleDir
|
||||||
|
procCPUInfo = savedProcCPUInfo
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = os.MkdirAll(sysModuleDir, testDirMode)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuData := []testCPUData{
|
||||||
|
{"", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleData := []testModuleData{
|
||||||
|
{filepath.Join(sysModuleDir, "kvm"), false, "Y"},
|
||||||
|
}
|
||||||
|
|
||||||
|
devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0666)
|
||||||
|
assert.NoError(err)
|
||||||
|
defer devNull.Close()
|
||||||
|
|
||||||
|
savedLogOutput := kataLog.Logger.Out
|
||||||
|
|
||||||
|
// discard normal output
|
||||||
|
kataLog.Logger.Out = devNull
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
kataLog.Logger.Out = savedLogOutput
|
||||||
|
}()
|
||||||
|
|
||||||
|
setupCheckHostIsVMContainerCapable(assert, cpuInfoFile, cpuData, moduleData)
|
||||||
|
|
||||||
|
ctx := createCLIContext(nil)
|
||||||
|
ctx.App.Name = "foo"
|
||||||
|
|
||||||
|
// create buffer to save logger output
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// capture output this time
|
||||||
|
kataLog.Logger.Out = buf
|
||||||
|
|
||||||
|
fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error)
|
||||||
|
assert.True(ok)
|
||||||
|
|
||||||
|
err = fn(ctx)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
for _, m := range moduleData {
|
||||||
|
name := path.Base(m.path)
|
||||||
|
assert.True(findAnchoredString(output, name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArchKernelParamHandler(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
type testData struct {
|
||||||
|
onVMM bool
|
||||||
|
fields logrus.Fields
|
||||||
|
msg string
|
||||||
|
expectIgnore bool
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []testData{
|
||||||
|
{true, logrus.Fields{}, "", false},
|
||||||
|
{false, logrus.Fields{}, "", false},
|
||||||
|
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
logrus.Fields{
|
||||||
|
// wrong type
|
||||||
|
"parameter": 123,
|
||||||
|
},
|
||||||
|
"foo",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
logrus.Fields{
|
||||||
|
"parameter": "unrestricted_guest",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
logrus.Fields{
|
||||||
|
"parameter": "unrestricted_guest",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
logrus.Fields{
|
||||||
|
"parameter": "nested",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, d := range data {
|
||||||
|
result := archKernelParamHandler(d.onVMM, d.fields, d.msg)
|
||||||
|
if d.expectIgnore {
|
||||||
|
assert.True(result, "test %d (%+v)", i, d)
|
||||||
|
} else {
|
||||||
|
assert.False(result, "test %d (%+v)", i, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKvmIsUsable(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
savedKvmDevice := kvmDevice
|
||||||
|
fakeKVMDevice := filepath.Join(dir, "kvm")
|
||||||
|
kvmDevice = fakeKVMDevice
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
kvmDevice = savedKvmDevice
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = kvmIsUsable()
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
err = createEmptyFile(fakeKVMDevice)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
err = kvmIsUsable()
|
||||||
|
assert.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestDataa struct {
|
||||||
|
contents string
|
||||||
|
expectedVendor string
|
||||||
|
expectedModel string
|
||||||
|
expectError bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCPUDetails(t *testing.T) {
|
||||||
|
type testData struct {
|
||||||
|
contents string
|
||||||
|
expectedVendor string
|
||||||
|
expectedModel string
|
||||||
|
expectError bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const validVendorName = "a vendor"
|
||||||
|
validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName)
|
||||||
|
|
||||||
|
const validModelName = "some CPU model"
|
||||||
|
validModel := fmt.Sprintf(`processor 0: version = 00, identification = XXXXX, %s = %s`, archCPUModelField, validModelName)
|
||||||
|
|
||||||
|
validContents := fmt.Sprintf(`
|
||||||
|
a : b
|
||||||
|
%s
|
||||||
|
foo : bar
|
||||||
|
%s
|
||||||
|
`, validVendor, validModel)
|
||||||
|
|
||||||
|
data := []TestDataa{
|
||||||
|
{"", "", "", true},
|
||||||
|
{"invalid", "", "", true},
|
||||||
|
{archCPUVendorField, "", "", true},
|
||||||
|
{validVendor, "", "", true},
|
||||||
|
{validModel, "", "", true},
|
||||||
|
{validContents, validVendorName, validModelName, false},
|
||||||
|
}
|
||||||
|
genericTestGetCPUDetails(t, validVendor, validModel, validContents, data)
|
||||||
|
}
|
91
cli/kata-env_s390x_test.go
Normal file
91
cli/kata-env_s390x_test.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (c) 2018 IBM
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
vcUtils "github.com/kata-containers/runtime/virtcontainers/utils"
|
||||||
|
"path/filepath"
|
||||||
|
goruntime "runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getExpectedHostDetails(tmpdir string) (HostInfo, error) {
|
||||||
|
type filesToCreate struct {
|
||||||
|
file string
|
||||||
|
contents string
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedKernelVersion = "99.1"
|
||||||
|
const expectedArch = goruntime.GOARCH
|
||||||
|
|
||||||
|
expectedDistro := DistroInfo{
|
||||||
|
Name: "Foo",
|
||||||
|
Version: "42",
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedCPU := CPUInfo{
|
||||||
|
Vendor: "moi",
|
||||||
|
Model: "awesome XI",
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedHostDetails := HostInfo{
|
||||||
|
Kernel: expectedKernelVersion,
|
||||||
|
Architecture: expectedArch,
|
||||||
|
Distro: expectedDistro,
|
||||||
|
CPU: expectedCPU,
|
||||||
|
VMContainerCapable: true,
|
||||||
|
SupportVSocks: vcUtils.SupportsVsocks(),
|
||||||
|
}
|
||||||
|
|
||||||
|
testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo")
|
||||||
|
testOSRelease := filepath.Join(tmpdir, "os-release")
|
||||||
|
|
||||||
|
// XXX: This file is *NOT* created by this function on purpose
|
||||||
|
// (to ensure the only file checked by the tests is
|
||||||
|
// testOSRelease). osReleaseClr handling is tested in
|
||||||
|
// utils_test.go.
|
||||||
|
testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr")
|
||||||
|
|
||||||
|
testProcVersion := filepath.Join(tmpdir, "proc-version")
|
||||||
|
|
||||||
|
// override
|
||||||
|
procVersion = testProcVersion
|
||||||
|
osRelease = testOSRelease
|
||||||
|
osReleaseClr = testOSReleaseClr
|
||||||
|
procCPUInfo = testProcCPUInfo
|
||||||
|
|
||||||
|
procVersionContents := fmt.Sprintf("Linux version %s a b c",
|
||||||
|
expectedKernelVersion)
|
||||||
|
|
||||||
|
osReleaseContents := fmt.Sprintf(`
|
||||||
|
NAME="%s"
|
||||||
|
VERSION_ID="%s"
|
||||||
|
`, expectedDistro.Name, expectedDistro.Version)
|
||||||
|
|
||||||
|
procCPUInfoContents := fmt.Sprintf(`
|
||||||
|
%s : %s
|
||||||
|
processor 0: version = 00, identification = 3929E7, %s = %s
|
||||||
|
`,
|
||||||
|
archCPUVendorField,
|
||||||
|
expectedCPU.Vendor,
|
||||||
|
archCPUModelField,
|
||||||
|
expectedCPU.Model)
|
||||||
|
|
||||||
|
data := []filesToCreate{
|
||||||
|
{procVersion, procVersionContents},
|
||||||
|
{osRelease, osReleaseContents},
|
||||||
|
{procCPUInfo, procCPUInfoContents},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range data {
|
||||||
|
err := createFile(d.file, d.contents)
|
||||||
|
if err != nil {
|
||||||
|
return HostInfo{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expectedHostDetails, nil
|
||||||
|
}
|
10
cli/utils_s390x.go
Normal file
10
cli/utils_s390x.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright (c) 2018 IBM
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func archConvertStatFs(cgroupFsType int) uint32 {
|
||||||
|
return uint32(cgroupFsType)
|
||||||
|
}
|
@@ -97,6 +97,9 @@ assets:
|
|||||||
ppc64le:
|
ppc64le:
|
||||||
name: "centos"
|
name: "centos"
|
||||||
version: "latest"
|
version: "latest"
|
||||||
|
s390x:
|
||||||
|
name: "ubuntu"
|
||||||
|
version: "latest"
|
||||||
x86_64:
|
x86_64:
|
||||||
name: &default-image-name "clearlinux"
|
name: &default-image-name "clearlinux"
|
||||||
version: "20640"
|
version: "20640"
|
||||||
@@ -115,6 +118,9 @@ assets:
|
|||||||
ppc64le:
|
ppc64le:
|
||||||
name: *default-initrd-name
|
name: *default-initrd-name
|
||||||
version: *default-initrd-version
|
version: *default-initrd-version
|
||||||
|
s390x:
|
||||||
|
name: *default-initrd-name
|
||||||
|
version: *default-initrd-version
|
||||||
x86_64:
|
x86_64:
|
||||||
name: *default-initrd-name
|
name: *default-initrd-name
|
||||||
version: *default-initrd-version
|
version: *default-initrd-version
|
||||||
|
@@ -525,7 +525,7 @@ func getHostMemorySizeKb(memInfoPath string) (uint64, error) {
|
|||||||
|
|
||||||
// RunningOnVMM checks if the system is running inside a VM.
|
// RunningOnVMM checks if the system is running inside a VM.
|
||||||
func RunningOnVMM(cpuInfoPath string) (bool, error) {
|
func RunningOnVMM(cpuInfoPath string) (bool, error) {
|
||||||
if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" {
|
if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" {
|
||||||
virtLog.Info("Unable to know if the system is running inside a VM")
|
virtLog.Info("Unable to know if the system is running inside a VM")
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@@ -872,8 +872,8 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var tap TapInterface
|
var tap TapInterface
|
||||||
|
devID := "virtio-" + tap.ID
|
||||||
|
|
||||||
if op == addDevice {
|
|
||||||
switch endpoint.Type() {
|
switch endpoint.Type() {
|
||||||
case VethEndpointType:
|
case VethEndpointType:
|
||||||
drive := endpoint.(*VethEndpoint)
|
drive := endpoint.(*VethEndpoint)
|
||||||
@@ -885,6 +885,8 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error {
|
|||||||
return fmt.Errorf("this endpoint is not supported")
|
return fmt.Errorf("this endpoint is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if op == addDevice {
|
||||||
|
|
||||||
if err = q.hotAddNetDevice(tap.Name, endpoint.HardwareAddr(), tap.VMFds, tap.VhostFds); err != nil {
|
if err = q.hotAddNetDevice(tap.Name, endpoint.HardwareAddr(), tap.VMFds, tap.VhostFds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -896,34 +898,28 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error {
|
|||||||
pciAddr := fmt.Sprintf("%02x/%s", bridge.Addr, addr)
|
pciAddr := fmt.Sprintf("%02x/%s", bridge.Addr, addr)
|
||||||
endpoint.SetPciAddr(pciAddr)
|
endpoint.SetPciAddr(pciAddr)
|
||||||
|
|
||||||
devID := "virtio-" + tap.ID
|
var machine govmmQemu.Machine
|
||||||
if err = q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs), q.arch.runNested()); err != nil {
|
machine, err = q.getQemuMachine()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
if machine.Type == QemuCCWVirtio {
|
||||||
switch endpoint.Type() {
|
return q.qmpMonitorCh.qmp.ExecuteNetCCWDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, int(q.config.NumVCPUs))
|
||||||
case VethEndpointType:
|
}
|
||||||
drive := endpoint.(*VethEndpoint)
|
return q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs), q.arch.runNested())
|
||||||
tap = drive.NetPair.TapInterface
|
|
||||||
case TapEndpointType:
|
|
||||||
drive := endpoint.(*TapEndpoint)
|
|
||||||
tap = drive.TapInterface
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("this endpoint is not supported")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := q.removeDeviceFromBridge(tap.ID); err != nil {
|
if err := q.removeDeviceFromBridge(tap.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
devID := "virtio-" + tap.ID
|
|
||||||
if err := q.qmpMonitorCh.qmp.ExecuteDeviceDel(q.qmpMonitorCh.ctx, devID); err != nil {
|
if err := q.qmpMonitorCh.qmp.ExecuteDeviceDel(q.qmpMonitorCh.ctx, devID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := q.qmpMonitorCh.qmp.ExecuteNetdevDel(q.qmpMonitorCh.ctx, tap.Name); err != nil {
|
if err := q.qmpMonitorCh.qmp.ExecuteNetdevDel(q.qmpMonitorCh.ctx, tap.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1149,6 +1145,7 @@ func (q *qemu) resumeSandbox() error {
|
|||||||
|
|
||||||
// addDevice will add extra devices to Qemu command line.
|
// addDevice will add extra devices to Qemu command line.
|
||||||
func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
|
func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
|
||||||
|
var err error
|
||||||
span, _ := q.trace("addDevice")
|
span, _ := q.trace("addDevice")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
@@ -1165,14 +1162,14 @@ func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
|
|||||||
case config.BlockDrive:
|
case config.BlockDrive:
|
||||||
q.qemuConfig.Devices = q.arch.appendBlockDevice(q.qemuConfig.Devices, v)
|
q.qemuConfig.Devices = q.arch.appendBlockDevice(q.qemuConfig.Devices, v)
|
||||||
case config.VhostUserDeviceAttrs:
|
case config.VhostUserDeviceAttrs:
|
||||||
q.qemuConfig.Devices = q.arch.appendVhostUserDevice(q.qemuConfig.Devices, v)
|
q.qemuConfig.Devices, err = q.arch.appendVhostUserDevice(q.qemuConfig.Devices, v)
|
||||||
case config.VFIODev:
|
case config.VFIODev:
|
||||||
q.qemuConfig.Devices = q.arch.appendVFIODevice(q.qemuConfig.Devices, v)
|
q.qemuConfig.Devices = q.arch.appendVFIODevice(q.qemuConfig.Devices, v)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSandboxConsole builds the path of the console where we can read
|
// getSandboxConsole builds the path of the console where we can read
|
||||||
@@ -1293,6 +1290,8 @@ func genericBridges(number uint32, machineType string) []Bridge {
|
|||||||
bt = pcieBridge
|
bt = pcieBridge
|
||||||
case QemuPseries:
|
case QemuPseries:
|
||||||
bt = pciBridge
|
bt = pciBridge
|
||||||
|
case QemuCCWVirtio:
|
||||||
|
bt = pciBridge
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -86,7 +86,7 @@ type qemuArch interface {
|
|||||||
appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) []govmmQemu.Device
|
appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) []govmmQemu.Device
|
||||||
|
|
||||||
// appendVhostUserDevice appends a vhost user device to devices
|
// appendVhostUserDevice appends a vhost user device to devices
|
||||||
appendVhostUserDevice(devices []govmmQemu.Device, drive config.VhostUserDeviceAttrs) []govmmQemu.Device
|
appendVhostUserDevice(devices []govmmQemu.Device, drive config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error)
|
||||||
|
|
||||||
// appendVFIODevice appends a VFIO device to devices
|
// appendVFIODevice appends a VFIO device to devices
|
||||||
appendVFIODevice(devices []govmmQemu.Device, vfioDevice config.VFIODev) []govmmQemu.Device
|
appendVFIODevice(devices []govmmQemu.Device, vfioDevice config.VFIODev) []govmmQemu.Device
|
||||||
@@ -151,6 +151,9 @@ const (
|
|||||||
|
|
||||||
// QemuPseries is a QEMU virt machine type for ppc64le
|
// QemuPseries is a QEMU virt machine type for ppc64le
|
||||||
QemuPseries = "pseries"
|
QemuPseries = "pseries"
|
||||||
|
|
||||||
|
// QemuCCWVirtio is a QEMU virt machine type for for s390x
|
||||||
|
QemuCCWVirtio = "s390-ccw-virtio"
|
||||||
)
|
)
|
||||||
|
|
||||||
// kernelParamsNonDebug is a list of the default kernel
|
// kernelParamsNonDebug is a list of the default kernel
|
||||||
@@ -456,7 +459,7 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi
|
|||||||
devices = append(devices,
|
devices = append(devices,
|
||||||
govmmQemu.NetDevice{
|
govmmQemu.NetDevice{
|
||||||
Type: networkModelToQemuType(netPair.NetInterworkingModel),
|
Type: networkModelToQemuType(netPair.NetInterworkingModel),
|
||||||
Driver: govmmQemu.VirtioNetPCI,
|
Driver: govmmQemu.VirtioNet,
|
||||||
ID: fmt.Sprintf("network-%d", q.networkIndex),
|
ID: fmt.Sprintf("network-%d", q.networkIndex),
|
||||||
IFName: netPair.TAPIface.Name,
|
IFName: netPair.TAPIface.Name,
|
||||||
MACAddress: netPair.TAPIface.HardAddr,
|
MACAddress: netPair.TAPIface.HardAddr,
|
||||||
@@ -473,7 +476,7 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi
|
|||||||
devices = append(devices,
|
devices = append(devices,
|
||||||
govmmQemu.NetDevice{
|
govmmQemu.NetDevice{
|
||||||
Type: govmmQemu.MACVTAP,
|
Type: govmmQemu.MACVTAP,
|
||||||
Driver: govmmQemu.VirtioNetPCI,
|
Driver: govmmQemu.VirtioNet,
|
||||||
ID: fmt.Sprintf("network-%d", q.networkIndex),
|
ID: fmt.Sprintf("network-%d", q.networkIndex),
|
||||||
IFName: ep.Name(),
|
IFName: ep.Name(),
|
||||||
MACAddress: ep.HardwareAddr(),
|
MACAddress: ep.HardwareAddr(),
|
||||||
@@ -516,7 +519,7 @@ func (q *qemuArchBase) appendBlockDevice(devices []govmmQemu.Device, drive confi
|
|||||||
return devices
|
return devices
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) []govmmQemu.Device {
|
func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error) {
|
||||||
qemuVhostUserDevice := govmmQemu.VhostUserDevice{}
|
qemuVhostUserDevice := govmmQemu.VhostUserDevice{}
|
||||||
|
|
||||||
switch attr.Type {
|
switch attr.Type {
|
||||||
@@ -534,7 +537,7 @@ func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr co
|
|||||||
|
|
||||||
devices = append(devices, qemuVhostUserDevice)
|
devices = append(devices, qemuVhostUserDevice)
|
||||||
|
|
||||||
return devices
|
return devices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDev config.VFIODev) []govmmQemu.Device {
|
func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDev config.VFIODev) []govmmQemu.Device {
|
||||||
|
@@ -199,6 +199,7 @@ func TestQemuArchBaseMemoryTopology(t *testing.T) {
|
|||||||
|
|
||||||
func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govmmQemu.Device) {
|
func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govmmQemu.Device) {
|
||||||
var devices []govmmQemu.Device
|
var devices []govmmQemu.Device
|
||||||
|
var err error
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
qemuArchBase := newQemuArchBase()
|
qemuArchBase := newQemuArchBase()
|
||||||
|
|
||||||
@@ -212,9 +213,10 @@ func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govm
|
|||||||
case config.VFIODev:
|
case config.VFIODev:
|
||||||
devices = qemuArchBase.appendVFIODevice(devices, s)
|
devices = qemuArchBase.appendVFIODevice(devices, s)
|
||||||
case config.VhostUserDeviceAttrs:
|
case config.VhostUserDeviceAttrs:
|
||||||
devices = qemuArchBase.appendVhostUserDevice(devices, s)
|
devices, err = qemuArchBase.appendVhostUserDevice(devices, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.NoError(err)
|
||||||
assert.Equal(devices, expected)
|
assert.Equal(devices, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +473,7 @@ func TestQemuArchBaseAppendNetwork(t *testing.T) {
|
|||||||
expectedOut := []govmmQemu.Device{
|
expectedOut := []govmmQemu.Device{
|
||||||
govmmQemu.NetDevice{
|
govmmQemu.NetDevice{
|
||||||
Type: networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel),
|
Type: networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel),
|
||||||
Driver: govmmQemu.VirtioNetPCI,
|
Driver: govmmQemu.VirtioNet,
|
||||||
ID: fmt.Sprintf("network-%d", 0),
|
ID: fmt.Sprintf("network-%d", 0),
|
||||||
IFName: macvlanEp.NetPair.TAPIface.Name,
|
IFName: macvlanEp.NetPair.TAPIface.Name,
|
||||||
MACAddress: macvlanEp.NetPair.TAPIface.HardAddr,
|
MACAddress: macvlanEp.NetPair.TAPIface.HardAddr,
|
||||||
@@ -482,7 +484,7 @@ func TestQemuArchBaseAppendNetwork(t *testing.T) {
|
|||||||
},
|
},
|
||||||
govmmQemu.NetDevice{
|
govmmQemu.NetDevice{
|
||||||
Type: govmmQemu.MACVTAP,
|
Type: govmmQemu.MACVTAP,
|
||||||
Driver: govmmQemu.VirtioNetPCI,
|
Driver: govmmQemu.VirtioNet,
|
||||||
ID: fmt.Sprintf("network-%d", 1),
|
ID: fmt.Sprintf("network-%d", 1),
|
||||||
IFName: macvtapEp.Name(),
|
IFName: macvtapEp.Name(),
|
||||||
MACAddress: macvtapEp.HardwareAddr(),
|
MACAddress: macvtapEp.HardwareAddr(),
|
||||||
|
122
virtcontainers/qemu_s390x.go
Normal file
122
virtcontainers/qemu_s390x.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (c) 2018 IBM
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package virtcontainers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
govmmQemu "github.com/intel/govmm/qemu"
|
||||||
|
"github.com/kata-containers/runtime/virtcontainers/device/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type qemuS390x struct {
|
||||||
|
// inherit from qemuArchBase, overwrite methods if needed
|
||||||
|
qemuArchBase
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultQemuPath = "/usr/bin/qemu-system-s390x"
|
||||||
|
|
||||||
|
const defaultQemuMachineType = QemuCCWVirtio
|
||||||
|
|
||||||
|
const defaultQemuMachineOptions = "accel=kvm"
|
||||||
|
|
||||||
|
const defaultPCBridgeBus = "pci.0"
|
||||||
|
|
||||||
|
const VirtioSerialCCW = "virtio-serial-ccw"
|
||||||
|
|
||||||
|
var qemuPaths = map[string]string{
|
||||||
|
QemuCCWVirtio: defaultQemuPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
var kernelRootParams = []Param{}
|
||||||
|
|
||||||
|
// Verify needed parameters
|
||||||
|
var kernelParams = []Param{
|
||||||
|
{"console", "ttysclp0"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportedQemuMachines = []govmmQemu.Machine{
|
||||||
|
{
|
||||||
|
Type: QemuCCWVirtio,
|
||||||
|
Options: defaultQemuMachineOptions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxQemuVCPUs returns the maximum number of vCPUs supported
|
||||||
|
func MaxQemuVCPUs() uint32 {
|
||||||
|
// Max number of virtual Cpu defined in qemu. See
|
||||||
|
// https://github.com/qemu/qemu/blob/80422b00196a7af4c6efb628fae0ad8b644e98af/target/s390x/cpu.h#L55
|
||||||
|
// #define S390_MAX_CPUS 248
|
||||||
|
return uint32(248)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newQemuArch(config HypervisorConfig) qemuArch {
|
||||||
|
machineType := config.HypervisorMachineType
|
||||||
|
if machineType == "" {
|
||||||
|
machineType = defaultQemuMachineType
|
||||||
|
}
|
||||||
|
|
||||||
|
q := &qemuS390x{
|
||||||
|
qemuArchBase{
|
||||||
|
machineType: machineType,
|
||||||
|
qemuPaths: qemuPaths,
|
||||||
|
supportedQemuMachines: supportedQemuMachines,
|
||||||
|
kernelParamsNonDebug: kernelParamsNonDebug,
|
||||||
|
kernelParamsDebug: kernelParamsDebug,
|
||||||
|
kernelParams: kernelParams,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ImagePath != "" {
|
||||||
|
q.kernelParams = append(q.kernelParams, kernelRootParams...)
|
||||||
|
q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...)
|
||||||
|
q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendBridges appends to devices the given bridges
|
||||||
|
func (q *qemuS390x) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device {
|
||||||
|
return genericAppendBridges(devices, bridges, q.machineType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendConsole appends a console to devices.
|
||||||
|
// The function has been overwriten to correctly set the driver to the CCW device
|
||||||
|
func (q *qemuS390x) appendConsole(devices []govmmQemu.Device, path string) []govmmQemu.Device {
|
||||||
|
serial := govmmQemu.SerialDevice{
|
||||||
|
Driver: VirtioSerialCCW,
|
||||||
|
ID: "serial0",
|
||||||
|
DisableModern: q.nestedRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = append(devices, serial)
|
||||||
|
|
||||||
|
var console govmmQemu.CharDevice
|
||||||
|
|
||||||
|
console = govmmQemu.CharDevice{
|
||||||
|
Driver: govmmQemu.Console,
|
||||||
|
Backend: govmmQemu.Socket,
|
||||||
|
DeviceID: "console0",
|
||||||
|
ID: "charconsole0",
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = append(devices, console)
|
||||||
|
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendVhostUserDevice throws an error if vhost devices are tried to be used.
|
||||||
|
// See issue https://github.com/kata-containers/runtime/issues/659
|
||||||
|
func (q *qemuS390x) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error) {
|
||||||
|
return nil, fmt.Errorf("No vhost-user devices supported on s390x")
|
||||||
|
}
|
||||||
|
|
||||||
|
// supportGuestMemoryHotplug return false for s390x architecture. The pc-dimm backend device for s390x
|
||||||
|
// is not support. PC-DIMM is not listed in the devices supported by qemu-system-s390x -device help
|
||||||
|
func (q *qemuS390x) supportGuestMemoryHotplug() bool {
|
||||||
|
return false
|
||||||
|
}
|
68
virtcontainers/qemu_s390x_test.go
Normal file
68
virtcontainers/qemu_s390x_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2018 IBM
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package virtcontainers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
govmmQemu "github.com/intel/govmm/qemu"
|
||||||
|
"github.com/kata-containers/runtime/virtcontainers/device/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestQemu(machineType string) qemuArch {
|
||||||
|
config := HypervisorConfig{
|
||||||
|
HypervisorMachineType: machineType,
|
||||||
|
}
|
||||||
|
return newQemuArch(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQemuS390xCPUModel(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
s390x := newTestQemu(QemuCCWVirtio)
|
||||||
|
|
||||||
|
expectedOut := defaultCPUModel
|
||||||
|
model := s390x.cpuModel()
|
||||||
|
assert.Equal(expectedOut, model)
|
||||||
|
|
||||||
|
s390x.enableNestingChecks()
|
||||||
|
expectedOut = defaultCPUModel + ",pmu=off"
|
||||||
|
model = s390x.cpuModel()
|
||||||
|
assert.Equal(expectedOut, model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQemuS390xMemoryTopology(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
s390x := newTestQemu(QemuCCWVirtio)
|
||||||
|
memoryOffset := 1024
|
||||||
|
|
||||||
|
hostMem := uint64(1024)
|
||||||
|
mem := uint64(120)
|
||||||
|
slots := uint8(10)
|
||||||
|
expectedMemory := govmmQemu.Memory{
|
||||||
|
Size: fmt.Sprintf("%dM", mem),
|
||||||
|
Slots: slots,
|
||||||
|
MaxMem: fmt.Sprintf("%dM", hostMem+uint64(memoryOffset)),
|
||||||
|
}
|
||||||
|
|
||||||
|
m := s390x.memoryTopology(mem, hostMem, slots)
|
||||||
|
assert.Equal(expectedMemory, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQemuS390xAppendVhostUserDevice(t *testing.T) {
|
||||||
|
macAddress := "00:11:22:33:44:55:66"
|
||||||
|
qemu := qemuS390x{}
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
vhostUserDevice := config.VhostUserDeviceAttrs{
|
||||||
|
Type: config.VhostUserNet,
|
||||||
|
MacAddress: macAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := qemu.appendVhostUserDevice(nil, vhostUserDevice)
|
||||||
|
assert.Error(err)
|
||||||
|
}
|
Reference in New Issue
Block a user