kata-check: Check and validate type-1 hypervisor for kata

ACRN hypervisor is a type-1 hypervisor and this patch
adds support to check and validate if the system is
capable of running kata containers with ACRN hypervisor.

Depends-on: github.com/kata-containers/tests#1793

v3->v4:
Implemented a generic way to identify hypervisor and
test VM creation.

v2->v3:
1. Removed cgo structs and defined go structs.
2. Suppressed lint warnings due to unused createVM struct.

v1->v2:
1. Created an issue #1784 to address TODO item.
2. Fixed formatting of the log message.
3. Currently ACRN is only supported on amd64. So
   moved ACRN specific code to kata-check_amd64.go.

Fixes: #1778

Signed-off-by: Vijay Dhanraj <vijay.dhanraj@intel.com>
This commit is contained in:
Vijay Dhanraj 2019-06-08 12:42:31 -07:00
parent 4d26ceee79
commit adcac9368f
13 changed files with 279 additions and 108 deletions

View File

@ -16,6 +16,7 @@ const int ioctl_KVM_CHECK_EXTENSION = KVM_CHECK_EXTENSION;
import "C"
import (
"errors"
"fmt"
"os"
"os/exec"
@ -26,6 +27,7 @@ import (
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@ -311,7 +313,12 @@ var kataCheckCLICommand = cli.Command{
span, _ := katautils.Trace(ctx, "kata-check")
defer span.Finish()
err = setCPUtype()
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
if !ok {
return errors.New("kata-check: cannot determine runtime config")
}
err = setCPUtype(runtimeConfig.HypervisorType)
if err != nil {
return err
}
@ -332,7 +339,7 @@ var kataCheckCLICommand = cli.Command{
kataLog.Info(successMessageCapable)
if os.Geteuid() == 0 {
err = archHostCanCreateVMContainer()
err = archHostCanCreateVMContainer(runtimeConfig.HypervisorType)
if err != nil {
return err
}

View File

@ -7,11 +7,13 @@ package main
import (
"fmt"
"github.com/sirupsen/logrus"
"io/ioutil"
"strings"
"syscall"
"unsafe"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus"
)
const (
@ -24,6 +26,17 @@ const (
msgKernelVirtio = "Host kernel accelerator for virtio"
msgKernelVirtioNet = "Host kernel accelerator for virtio network"
msgKernelVirtioVhostVsock = "Host Support for Linux VM Sockets"
cpuFlagVMX = "vmx"
cpuFlagLM = "lm"
cpuFlagSVM = "svm"
cpuFlagSSE4_1 = "sse4_1"
kernelModvhm = "vhm_dev"
kernelModvhost = "vhost"
kernelModvhostnet = "vhost_net"
kernelModvhostvsock = "vhost_vsock"
kernelModkvm = "kvm"
kernelModkvmintel = "kvm_intel"
kernelModkvmamd = "kvm_amd"
)
// CPU types
@ -33,6 +46,28 @@ const (
cpuTypeUnknown = -1
)
const acrnDevice = "/dev/acrn_vhm"
// ioctl_ACRN_CREATE_VM is the IOCTL to create VM in ACRN.
// Current Linux mainstream kernel doesn't have support for ACRN.
// Due to this several macros are not defined in Linux headers.
// Until the support is available, directly use the value instead
// of macros.
//https://github.com/kata-containers/runtime/issues/1784
const ioctl_ACRN_CREATE_VM = 0x43000010 //nolint
const ioctl_ACRN_DESTROY_VM = 0x43000011 //nolint
type acrn_create_vm struct { //nolint
vmid uint16 //nolint
reserved0 uint16 //nolint
vcpu_num uint16 //nolint
reserved1 uint16 //nolint
uuid [16]uint8
vm_flag uint64 //nolint
req_buf uint64 //nolint
reserved2 [16]uint8 //nolint
}
// cpuType save the CPU type
var cpuType int
@ -49,7 +84,7 @@ var archRequiredCPUAttribs map[string]string
// required module parameters.
var archRequiredKernelModules map[string]kernelModule
func setCPUtype() error {
func setCPUtype(hypervisorType vc.HypervisorType) error {
cpuType = getCPUtype()
if cpuType == cpuTypeUnknown {
@ -66,64 +101,88 @@ func setCPUtype() error {
"unrestricted_guest": "Y",
}
}
switch hypervisorType {
case "firecracker":
fallthrough
case "qemu":
archRequiredCPUFlags = map[string]string{
"vmx": "Virtualization support",
"lm": "64Bit CPU",
"sse4_1": "SSE4.1",
cpuFlagVMX: "Virtualization support",
cpuFlagLM: "64Bit CPU",
cpuFlagSSE4_1: "SSE4.1",
}
archRequiredCPUAttribs = map[string]string{
archGenuineIntel: "Intel Architecture CPU",
}
archRequiredKernelModules = map[string]kernelModule{
"kvm": {
kernelModkvm: {
desc: msgKernelVM,
required: true,
},
"kvm_intel": {
kernelModkvmintel: {
desc: "Intel KVM",
parameters: kvmIntelParams,
required: true,
},
"vhost": {
kernelModvhost: {
desc: msgKernelVirtio,
required: true,
},
"vhost_net": {
kernelModvhostnet: {
desc: msgKernelVirtioNet,
required: true,
},
"vhost_vsock": {
kernelModvhostvsock: {
desc: msgKernelVirtioVhostVsock,
required: false,
},
}
case "acrn":
archRequiredCPUFlags = map[string]string{
cpuFlagLM: "64Bit CPU",
cpuFlagSSE4_1: "SSE4.1",
}
archRequiredCPUAttribs = map[string]string{
archGenuineIntel: "Intel Architecture CPU",
}
archRequiredKernelModules = map[string]kernelModule{
kernelModvhm: {
desc: "Intel ACRN",
},
kernelModvhost: {
desc: msgKernelVirtio,
},
kernelModvhostnet: {
desc: msgKernelVirtioNet,
},
}
default:
return fmt.Errorf("setCPUtype: Unknown hypervisor type %s", hypervisorType)
}
} else if cpuType == cpuTypeAMD {
archRequiredCPUFlags = map[string]string{
"svm": "Virtualization support",
"lm": "64Bit CPU",
"sse4_1": "SSE4.1",
cpuFlagSVM: "Virtualization support",
cpuFlagLM: "64Bit CPU",
cpuFlagSSE4_1: "SSE4.1",
}
archRequiredCPUAttribs = map[string]string{
archAuthenticAMD: "AMD Architecture CPU",
}
archRequiredKernelModules = map[string]kernelModule{
"kvm": {
kernelModkvm: {
desc: msgKernelVM,
required: true,
},
"kvm_amd": {
kernelModkvmamd: {
desc: "AMD KVM",
required: true,
},
"vhost": {
kernelModvhost: {
desc: msgKernelVirtio,
required: true,
},
"vhost_net": {
kernelModvhostnet: {
desc: msgKernelVirtioNet,
required: true,
},
"vhost_vsock": {
kernelModvhostvsock: {
desc: msgKernelVirtioVhostVsock,
required: false,
},
@ -155,8 +214,72 @@ func kvmIsUsable() error {
return genericKvmIsUsable()
}
func archHostCanCreateVMContainer() error {
// acrnIsUsable determines if it will be possible to create a full virtual machine
// by creating a minimal VM and then deleting it.
func acrnIsUsable() error {
flags := syscall.O_RDWR | syscall.O_CLOEXEC
f, err := syscall.Open(acrnDevice, flags, 0)
if err != nil {
return err
}
defer syscall.Close(f)
fieldLogger := kataLog.WithField("check-type", "full")
fieldLogger.WithField("device", acrnDevice).Info("device available")
createVM := acrn_create_vm{
uuid: [16]uint8{
0xd2, 0x79, 0x54, 0x38, 0x25, 0xd6, 0x11, 0xe8,
0x86, 0x4e, 0xcb, 0x7a, 0x18, 0xb3, 0x46, 0x43,
},
}
ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
uintptr(f),
uintptr(ioctl_ACRN_CREATE_VM),
uintptr(unsafe.Pointer(&createVM)))
if ret != 0 || errno != 0 {
if errno == syscall.EBUSY {
fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM")
}
fieldLogger.WithFields(logrus.Fields{
"ret": ret,
"errno": errno,
}).Info("Create VM Error")
return errno
}
ret, _, errno = syscall.Syscall(syscall.SYS_IOCTL,
uintptr(f),
uintptr(ioctl_ACRN_DESTROY_VM),
0)
if ret != 0 || errno != 0 {
fieldLogger.WithFields(logrus.Fields{
"ret": ret,
"errno": errno,
}).Info("Destroy VM Error")
return errno
}
fieldLogger.WithField("feature", "create-vm").Info("feature available")
return nil
}
func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error {
switch hypervisorType {
case "qemu":
fallthrough
case "firecracker":
return kvmIsUsable()
case "acrn":
return acrnIsUsable()
default:
return fmt.Errorf("archHostCanCreateVMContainer: Unknown hypervisor type %s", hypervisorType)
}
}
// hostIsVMContainerCapable checks to see if the host is theoretically capable

View File

@ -55,6 +55,9 @@ func TestCCCheckCLIFunction(t *testing.T) {
}
defer os.RemoveAll(dir)
_, config, err := makeRuntimeConfig(dir)
assert.NoError(err)
savedSysModuleDir := sysModuleDir
savedProcCPUInfo := procCPUInfo
@ -108,6 +111,7 @@ func TestCCCheckCLIFunction(t *testing.T) {
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = config
// create buffer to save logger output
buf := &bytes.Buffer{}
@ -514,6 +518,10 @@ foo : bar
func TestSetCPUtype(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
savedArchRequiredCPUFlags := archRequiredCPUFlags
savedArchRequiredCPUAttribs := archRequiredCPUAttribs
savedArchRequiredKernelModules := archRequiredKernelModules
@ -528,7 +536,10 @@ func TestSetCPUtype(t *testing.T) {
archRequiredCPUAttribs = map[string]string{}
archRequiredKernelModules = map[string]kernelModule{}
setCPUtype()
_, config, err := makeRuntimeConfig(tmpdir)
assert.NoError(err)
setCPUtype(config.HypervisorType)
assert.NotEmpty(archRequiredCPUFlags)
assert.NotEmpty(archRequiredCPUAttribs)

View File

@ -8,6 +8,7 @@ package main
import (
"fmt"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus"
)
@ -56,7 +57,7 @@ var archRequiredKVMExtensions = map[string]kvmExtension{
},
}
func setCPUtype() error {
func setCPUtype(hypervisorType vc.HypervisorType) error {
return nil
}
@ -84,7 +85,7 @@ func checkKVMExtensions() error {
return nil
}
func archHostCanCreateVMContainer() error {
func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error {
if err := kvmIsUsable(); err != nil {
return err
}

View File

@ -36,6 +36,9 @@ func TestCCCheckCLIFunction(t *testing.T) {
}
defer os.RemoveAll(dir)
_, config, err := makeRuntimeConfig(dir)
assert.NoError(err)
savedSysModuleDir := sysModuleDir
savedProcCPUInfo := procCPUInfo
@ -78,6 +81,7 @@ func TestCCCheckCLIFunction(t *testing.T) {
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = config
// create buffer to save logger output
buf := &bytes.Buffer{}

View File

@ -8,6 +8,8 @@
package main
import (
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
@ -16,6 +18,10 @@ import (
func testSetCPUTypeGeneric(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
savedArchRequiredCPUFlags := archRequiredCPUFlags
savedArchRequiredCPUAttribs := archRequiredCPUAttribs
savedArchRequiredKernelModules := archRequiredKernelModules
@ -30,7 +36,10 @@ func testSetCPUTypeGeneric(t *testing.T) {
assert.Empty(archRequiredCPUAttribs)
assert.NotEmpty(archRequiredKernelModules)
setCPUtype()
_, config, err := makeRuntimeConfig(tmpdir)
assert.NoError(err)
setCPUtype(config.HypervisorType)
assert.Equal(archRequiredCPUFlags, savedArchRequiredCPUFlags)
assert.Equal(archRequiredCPUAttribs, savedArchRequiredCPUAttribs)

View File

@ -8,12 +8,13 @@ package main
import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus"
"regexp"
"strconv"
)
const (
@ -55,11 +56,11 @@ var archRequiredKernelModules = map[string]kernelModule{
},
}
func setCPUtype() error {
func setCPUtype(hypervisorType vc.HypervisorType) error {
return nil
}
func archHostCanCreateVMContainer() error {
func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error {
return kvmIsUsable()
}

View File

@ -53,6 +53,9 @@ func TestCCCheckCLIFunction(t *testing.T) {
}
defer os.RemoveAll(dir)
_, config, err := makeRuntimeConfig(dir)
assert.NoError(err)
savedSysModuleDir := sysModuleDir
savedProcCPUInfo := procCPUInfo
@ -98,6 +101,7 @@ func TestCCCheckCLIFunction(t *testing.T) {
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = config
// create buffer to save logger output
buf := &bytes.Buffer{}

View File

@ -7,8 +7,10 @@ package main
import (
"fmt"
"github.com/sirupsen/logrus"
"strings"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus"
)
const (
@ -42,7 +44,7 @@ var archRequiredKernelModules = map[string]kernelModule{
},
}
func setCPUtype() error {
func setCPUtype(hypervisorType vc.HypervisorType) error {
return nil
}
@ -52,7 +54,7 @@ func kvmIsUsable() error {
return genericKvmIsUsable()
}
func archHostCanCreateVMContainer() error {
func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error {
return kvmIsUsable()
}

View File

@ -53,6 +53,9 @@ func TestCCCheckCLIFunction(t *testing.T) {
}
defer os.RemoveAll(dir)
_, config, err := makeRuntimeConfig(dir)
assert.NoError(err)
savedSysModuleDir := sysModuleDir
savedProcCPUInfo := procCPUInfo
@ -97,6 +100,7 @@ func TestCCCheckCLIFunction(t *testing.T) {
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = config
// create buffer to save logger output
buf := &bytes.Buffer{}

View File

@ -659,6 +659,9 @@ func TestCheckCLIFunctionFail(t *testing.T) {
}
defer os.RemoveAll(dir)
_, config, err := makeRuntimeConfig(dir)
assert.NoError(err)
oldProcCPUInfo := procCPUInfo
// doesn't exist
@ -670,6 +673,7 @@ func TestCheckCLIFunctionFail(t *testing.T) {
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = config
fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)

View File

@ -9,9 +9,8 @@ import (
"encoding/json"
"errors"
"os"
"strings"
runtim "runtime"
"strings"
"github.com/BurntSushi/toml"
"github.com/kata-containers/runtime/pkg/katautils"
@ -358,7 +357,7 @@ func getHypervisorInfo(config oci.RuntimeConfig) HypervisorInfo {
}
func getEnvInfo(configFile string, config oci.RuntimeConfig) (env EnvInfo, err error) {
err = setCPUtype()
err = setCPUtype(config.HypervisorType)
if err != nil {
return EnvInfo{}, err
}

View File

@ -254,14 +254,17 @@ func beforeSubcommands(c *cli.Context) error {
handleShowConfig(c)
if userWantsUsage(c) || (c.NArg() == 1 && (c.Args()[0] == checkCmd)) {
if userWantsUsage(c) {
// No setup required if the user just
// wants to see the usage statement or are
// running a command that does not manipulate
// containers.
// wants to see the usage statement.
return nil
}
ignoreLogging := false
var traceRootSpan string
subCmdIsCheckCmd := (c.NArg() == 1 && (c.Args()[0] == checkCmd))
if !subCmdIsCheckCmd {
if path := c.GlobalString("log"); path != "" {
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0640)
if err != nil {
@ -279,8 +282,6 @@ func beforeSubcommands(c *cli.Context) error {
return fmt.Errorf("unknown log-format %q", c.GlobalString("log-format"))
}
var traceRootSpan string
// Add the name of the sub-command to each log entry for easier
// debugging.
cmdName := c.Args().First()
@ -297,18 +298,18 @@ func beforeSubcommands(c *cli.Context) error {
// (meaning any spans created at this point will be silently ignored).
setExternalLoggers(context.Background(), kataLog)
ignoreLogging := false
if c.NArg() == 1 && c.Args()[0] == envCmd {
// simply report the logging setup
ignoreLogging = true
}
}
configFile, runtimeConfig, err = katautils.LoadConfiguration(c.GlobalString(configFilePathOption), ignoreLogging, false)
if err != nil {
fatal(err)
}
if !subCmdIsCheckCmd {
debug = runtimeConfig.Debug
crashOnError = runtimeConfig.Debug
@ -324,6 +325,7 @@ func beforeSubcommands(c *cli.Context) error {
return err
}
}
}
args := strings.Join(c.Args(), " ")