virtcontainers/cli: refactor code

Fixes #302

Signed-off-by: Nitesh Konkar niteshkonkar@in.ibm.com
This commit is contained in:
Nitesh Konkar 2018-05-31 17:58:35 +05:30
parent 9fb0b337ef
commit 4276c0c38e
6 changed files with 194 additions and 138 deletions

View File

@ -7,6 +7,13 @@
package main package main
/*
#include <linux/kvm.h>
const int ioctl_KVM_CREATE_VM = KVM_CREATE_VM;
*/
import "C"
import ( import (
"fmt" "fmt"
"os" "os"
@ -14,6 +21,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"syscall"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -51,6 +59,11 @@ var (
modInfoCmd = "modinfo" modInfoCmd = "modinfo"
) )
// variables rather than consts to allow tests to modify them
var (
kvmDevice = "/dev/kvm"
)
// getCPUInfo returns details of the first CPU read from the specified cpuinfo file // getCPUInfo returns details of the first CPU read from the specified cpuinfo file
func getCPUInfo(cpuInfoFile string) (string, error) { func getCPUInfo(cpuInfoFile string) (string, error) {
text, err := getFileContents(cpuInfoFile) text, err := getFileContents(cpuInfoFile)
@ -222,9 +235,9 @@ func checkKernelModules(modules map[string]kernelModule, handler kernelParamHand
return count, nil return count, nil
} }
// hostIsVMContainerCapable checks to see if the host is theoretically capable // genericHostIsVMContainerCapable checks to see if the host is theoretically capable
// of creating a VM container. // of creating a VM container.
func hostIsVMContainerCapable(details vmContainerCapableDetails) error { func genericHostIsVMContainerCapable(details vmContainerCapableDetails) error {
cpuinfo, err := getCPUInfo(details.cpuInfoFile) cpuinfo, err := getCPUInfo(details.cpuInfoFile)
if err != nil { if err != nil {
return err return err
@ -293,3 +306,58 @@ var kataCheckCLICommand = cli.Command{
return nil return nil
}, },
} }
func genericArchKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool {
param, ok := fields["parameter"].(string)
if !ok {
return false
}
// This option is not required when
// already running under a hypervisor.
if param == "unrestricted_guest" && onVMM {
kataLog.WithFields(fields).Warn(kernelPropertyCorrect)
return true
}
if param == "nested" {
kataLog.WithFields(fields).Warn(msg)
return true
}
// don't ignore the error
return false
}
// genericKvmIsUsable determines if it will be possible to create a full virtual machine
// by creating a minimal VM and then deleting it.
func genericKvmIsUsable() error {
flags := syscall.O_RDWR | syscall.O_CLOEXEC
f, err := syscall.Open(kvmDevice, flags, 0)
if err != nil {
return err
}
defer syscall.Close(f)
fieldLogger := kataLog.WithField("check-type", "full")
fieldLogger.WithField("device", kvmDevice).Info("device available")
vm, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
uintptr(f),
uintptr(C.ioctl_KVM_CREATE_VM),
0)
if errno != 0 {
if errno == syscall.EBUSY {
fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM")
}
return errno
}
defer syscall.Close(int(vm))
fieldLogger.WithField("feature", "create-vm").Info("feature available")
return nil
}

View File

@ -5,24 +5,10 @@
package main package main
/*
#include <linux/kvm.h>
const int ioctl_KVM_CREATE_VM = KVM_CREATE_VM;
*/
import "C"
import ( import (
"syscall"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// variables rather than consts to allow tests to modify them
var (
kvmDevice = "/dev/kvm"
)
// archRequiredCPUFlags maps a CPU flag value to search for and a // archRequiredCPUFlags maps a CPU flag value to search for and a
// human-readable description of that value. // human-readable description of that value.
var archRequiredCPUFlags = map[string]string{ var archRequiredCPUFlags = map[string]string{
@ -66,58 +52,19 @@ var archRequiredKernelModules = map[string]kernelModule{
// kvmIsUsable determines if it will be possible to create a full virtual machine // kvmIsUsable determines if it will be possible to create a full virtual machine
// by creating a minimal VM and then deleting it. // by creating a minimal VM and then deleting it.
func kvmIsUsable() error { func kvmIsUsable() error {
flags := syscall.O_RDWR | syscall.O_CLOEXEC return genericKvmIsUsable()
f, err := syscall.Open(kvmDevice, flags, 0)
if err != nil {
return err
}
defer syscall.Close(f)
fieldLogger := kataLog.WithField("check-type", "full")
fieldLogger.WithField("device", kvmDevice).Info("device available")
vm, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
uintptr(f),
uintptr(C.ioctl_KVM_CREATE_VM),
0)
if errno != 0 {
if errno == syscall.EBUSY {
fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM")
}
return errno
}
defer syscall.Close(int(vm))
fieldLogger.WithField("feature", "create-vm").Info("feature available")
return nil
} }
func archHostCanCreateVMContainer() error { func archHostCanCreateVMContainer() error {
return kvmIsUsable() return kvmIsUsable()
} }
// hostIsVMContainerCapable checks to see if the host is theoretically capable
// of creating a VM container.
func hostIsVMContainerCapable(details vmContainerCapableDetails) error {
return genericHostIsVMContainerCapable(details)
}
func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool {
param, ok := fields["parameter"].(string) return genericArchKernelParamHandler(onVMM, fields, msg)
if !ok {
return false
}
// This option is not required when
// already running under a hypervisor.
if param == "unrestricted_guest" && onVMM {
kataLog.WithFields(fields).Warn(kernelPropertyCorrect)
return true
}
if param == "nested" {
kataLog.WithFields(fields).Warn(msg)
return true
}
// don't ignore the error
return false
} }

22
cli/kata-check_arm64.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
// 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 {
return genericHostIsVMContainerCapable(details)
}

View File

@ -11,6 +11,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@ -926,3 +927,80 @@ func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
func (q *qemu) getSandboxConsole(sandboxID string) (string, error) { func (q *qemu) getSandboxConsole(sandboxID string) (string, error) {
return utils.BuildSocketPath(runStoragePath, sandboxID, defaultConsole) return utils.BuildSocketPath(runStoragePath, sandboxID, defaultConsole)
} }
// genericAppendBridges appends to devices the given bridges
func genericAppendBridges(devices []govmmQemu.Device, bridges []Bridge, machineType string) []govmmQemu.Device {
bus := defaultPCBridgeBus
if machineType == QemuQ35 {
bus = defaultBridgeBus
}
for idx, b := range bridges {
t := govmmQemu.PCIBridge
if b.Type == pcieBridge {
t = govmmQemu.PCIEBridge
}
bridges[idx].Addr = bridgePCIStartAddr + idx
devices = append(devices,
govmmQemu.BridgeDevice{
Type: t,
Bus: bus,
ID: b.ID,
// Each bridge is required to be assigned a unique chassis id > 0
Chassis: (idx + 1),
SHPC: true,
Addr: strconv.FormatInt(int64(bridges[idx].Addr), 10),
},
)
}
return devices
}
func genericBridges(number uint32, machineType string) []Bridge {
var bridges []Bridge
var bt bridgeType
switch machineType {
case QemuQ35:
// currently only pci bridges are supported
// qemu-2.10 will introduce pcie bridges
fallthrough
case QemuPC:
bt = pciBridge
default:
return nil
}
for i := uint32(0); i < number; i++ {
bridges = append(bridges, Bridge{
Type: bt,
ID: fmt.Sprintf("%s-bridge-%d", bt, i),
Address: make(map[uint32]string),
})
}
return bridges
}
func genericMemoryTopology(memoryMb, hostMemoryMb uint64) govmmQemu.Memory {
// NVDIMM device needs memory space 1024MB
// See https://github.com/clearcontainers/runtime/issues/380
memoryOffset := 1024
// add 1G memory space for nvdimm device (vm guest image)
memMax := fmt.Sprintf("%dM", hostMemoryMb+uint64(memoryOffset))
mem := fmt.Sprintf("%dM", memoryMb)
memory := govmmQemu.Memory{
Size: mem,
Slots: defaultMemSlots,
MaxMem: memMax,
}
return memory
}

View File

@ -6,9 +6,7 @@
package virtcontainers package virtcontainers
import ( import (
"fmt"
"os" "os"
"strconv"
govmmQemu "github.com/intel/govmm/qemu" govmmQemu "github.com/intel/govmm/qemu"
) )
@ -93,12 +91,7 @@ func newQemuArch(config HypervisorConfig) qemuArch {
}, },
} }
if config.ImagePath != "" { q.handleImagePath(config)
q.kernelParams = append(q.kernelParams, kernelRootParams...)
q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...)
q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...)
}
return q return q
} }
@ -114,29 +107,7 @@ func (q *qemuAmd64) capabilities() capabilities {
} }
func (q *qemuAmd64) bridges(number uint32) []Bridge { func (q *qemuAmd64) bridges(number uint32) []Bridge {
var bridges []Bridge return genericBridges(number, q.machineType)
var bt bridgeType
switch q.machineType {
case QemuQ35:
// currently only pci bridges are supported
// qemu-2.10 will introduce pcie bridges
fallthrough
case QemuPC:
bt = pciBridge
default:
return nil
}
for i := uint32(0); i < number; i++ {
bridges = append(bridges, Bridge{
Type: bt,
ID: fmt.Sprintf("%s-bridge-%d", bt, i),
Address: make(map[uint32]string),
})
}
return bridges
} }
func (q *qemuAmd64) cpuModel() string { func (q *qemuAmd64) cpuModel() string {
@ -148,22 +119,7 @@ func (q *qemuAmd64) cpuModel() string {
} }
func (q *qemuAmd64) memoryTopology(memoryMb, hostMemoryMb uint64) govmmQemu.Memory { func (q *qemuAmd64) memoryTopology(memoryMb, hostMemoryMb uint64) govmmQemu.Memory {
// NVDIMM device needs memory space 1024MB return genericMemoryTopology(memoryMb, hostMemoryMb)
// See https://github.com/clearcontainers/runtime/issues/380
memoryOffset := 1024
// add 1G memory space for nvdimm device (vm guest image)
memMax := fmt.Sprintf("%dM", hostMemoryMb+uint64(memoryOffset))
mem := fmt.Sprintf("%dM", memoryMb)
memory := govmmQemu.Memory{
Size: mem,
Slots: defaultMemSlots,
MaxMem: memMax,
}
return memory
} }
func (q *qemuAmd64) appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) { func (q *qemuAmd64) appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) {
@ -194,31 +150,5 @@ func (q *qemuAmd64) appendImage(devices []govmmQemu.Device, path string) ([]govm
// appendBridges appends to devices the given bridges // appendBridges appends to devices the given bridges
func (q *qemuAmd64) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device { func (q *qemuAmd64) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device {
bus := defaultPCBridgeBus return genericAppendBridges(devices, bridges, q.machineType)
if q.machineType == QemuQ35 {
bus = defaultBridgeBus
}
for idx, b := range bridges {
t := govmmQemu.PCIBridge
if b.Type == pcieBridge {
t = govmmQemu.PCIEBridge
}
bridges[idx].Addr = bridgePCIStartAddr + idx
devices = append(devices,
govmmQemu.BridgeDevice{
Type: t,
Bus: bus,
ID: b.ID,
// Each bridge is required to be assigned a unique chassis id > 0
Chassis: (idx + 1),
SHPC: true,
Addr: strconv.FormatInt(int64(bridges[idx].Addr), 10),
},
)
}
return devices
} }

View File

@ -82,6 +82,9 @@ type qemuArch interface {
// appendVFIODevice appends a VFIO device to devices // appendVFIODevice appends a VFIO device to devices
appendVFIODevice(devices []govmmQemu.Device, vfioDevice drivers.VFIODevice) []govmmQemu.Device appendVFIODevice(devices []govmmQemu.Device, vfioDevice drivers.VFIODevice) []govmmQemu.Device
// handleImagePath handles the Hypervisor Config image path
handleImagePath(config HypervisorConfig)
} }
type qemuArchBase struct { type qemuArchBase struct {
@ -495,3 +498,11 @@ func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDevice d
return devices return devices
} }
func (q *qemuArchBase) handleImagePath(config HypervisorConfig) {
if config.ImagePath != "" {
q.kernelParams = append(q.kernelParams, kernelRootParams...)
q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...)
q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...)
}
}