mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-10-21 11:58:41 +00:00
Added magic tags for `gometalinter` to ignore two unused `const`s that form part of an `iota` sequence. Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
501 lines
12 KiB
Go
501 lines
12 KiB
Go
//
|
|
// Copyright (c) 2016 Intel Corporation
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
package virtcontainers
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// HypervisorType describes an hypervisor type.
|
|
type HypervisorType string
|
|
|
|
const (
|
|
// QemuHypervisor is the QEMU hypervisor.
|
|
QemuHypervisor HypervisorType = "qemu"
|
|
|
|
// MockHypervisor is a mock hypervisor for testing purposes
|
|
MockHypervisor HypervisorType = "mock"
|
|
)
|
|
|
|
const (
|
|
procMemInfo = "/proc/meminfo"
|
|
procCPUInfo = "/proc/cpuinfo"
|
|
)
|
|
|
|
const (
|
|
defaultVCPUs = 1
|
|
// 2 GiB
|
|
defaultMemSzMiB = 2048
|
|
|
|
defaultBridges = 1
|
|
|
|
defaultBlockDriver = VirtioSCSI
|
|
)
|
|
|
|
// In some architectures the maximum number of vCPUs depends on the number of physical cores.
|
|
var defaultMaxQemuVCPUs = maxQemuVCPUs()
|
|
|
|
// deviceType describes a virtualized device type.
|
|
type deviceType int
|
|
|
|
const (
|
|
// ImgDev is the image device type.
|
|
imgDev deviceType = iota
|
|
|
|
// FsDev is the filesystem device type.
|
|
fsDev
|
|
|
|
// NetDev is the network device type.
|
|
netDev
|
|
|
|
// SerialDev is the serial device type.
|
|
serialDev // nolint: varcheck,unused
|
|
|
|
// BlockDev is the block device type.
|
|
blockDev
|
|
|
|
// ConsoleDev is the console device type.
|
|
consoleDev // nolint: varcheck,unused
|
|
|
|
// SerialPortDev is the serial port device type.
|
|
serialPortDev
|
|
|
|
// VFIODevice is VFIO device type
|
|
vfioDev
|
|
|
|
// vhostuserDev is a Vhost-user device type
|
|
vhostuserDev
|
|
|
|
// CPUDevice is CPU device type
|
|
cpuDev
|
|
)
|
|
|
|
// Set sets an hypervisor type based on the input string.
|
|
func (hType *HypervisorType) Set(value string) error {
|
|
switch value {
|
|
case "qemu":
|
|
*hType = QemuHypervisor
|
|
return nil
|
|
case "mock":
|
|
*hType = MockHypervisor
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("Unknown hypervisor type %s", value)
|
|
}
|
|
}
|
|
|
|
// String converts an hypervisor type to a string.
|
|
func (hType *HypervisorType) String() string {
|
|
switch *hType {
|
|
case QemuHypervisor:
|
|
return string(QemuHypervisor)
|
|
case MockHypervisor:
|
|
return string(MockHypervisor)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// newHypervisor returns an hypervisor from and hypervisor type.
|
|
func newHypervisor(hType HypervisorType) (hypervisor, error) {
|
|
switch hType {
|
|
case QemuHypervisor:
|
|
return &qemu{}, nil
|
|
case MockHypervisor:
|
|
return &mockHypervisor{}, nil
|
|
default:
|
|
return nil, fmt.Errorf("Unknown hypervisor type %s", hType)
|
|
}
|
|
}
|
|
|
|
//Generic function for creating a named-id for passing on the hypervisor commandline
|
|
func makeNameID(namedType string, id string) string {
|
|
nameID := fmt.Sprintf("%s-%s", namedType, id)
|
|
if len(nameID) > maxDevIDSize {
|
|
nameID = nameID[:maxDevIDSize]
|
|
}
|
|
|
|
return nameID
|
|
}
|
|
|
|
// Param is a key/value representation for hypervisor and kernel parameters.
|
|
type Param struct {
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
// HypervisorConfig is the hypervisor configuration.
|
|
type HypervisorConfig struct {
|
|
// KernelParams are additional guest kernel parameters.
|
|
KernelParams []Param
|
|
|
|
// HypervisorParams are additional hypervisor parameters.
|
|
HypervisorParams []Param
|
|
|
|
// KernelPath is the guest kernel host path.
|
|
KernelPath string
|
|
|
|
// ImagePath is the guest image host path.
|
|
ImagePath string
|
|
|
|
// FirmwarePath is the bios host path
|
|
FirmwarePath string
|
|
|
|
// MachineAccelerators are machine specific accelerators
|
|
MachineAccelerators string
|
|
|
|
// HypervisorPath is the hypervisor executable host path.
|
|
HypervisorPath string
|
|
|
|
// BlockDeviceDriver specifies the driver to be used for block device
|
|
// either VirtioSCSI or VirtioBlock with the default driver being defaultBlockDriver
|
|
BlockDeviceDriver string
|
|
|
|
// HypervisorMachineType specifies the type of machine being
|
|
// emulated.
|
|
HypervisorMachineType string
|
|
|
|
// customAssets is a map of assets.
|
|
// Each value in that map takes precedence over the configured assets.
|
|
// For example, if there is a value for the "kernel" key in this map,
|
|
// it will be used for the pod's kernel path instead of KernelPath.
|
|
customAssets map[assetType]*asset
|
|
|
|
// DefaultVCPUs specifies default number of vCPUs for the VM.
|
|
DefaultVCPUs uint32
|
|
|
|
//DefaultMaxVCPUs specifies the maximum number of vCPUs for the VM.
|
|
DefaultMaxVCPUs uint32
|
|
|
|
// DefaultMem specifies default memory size in MiB for the VM.
|
|
// Pod configuration VMConfig.Memory overwrites this.
|
|
DefaultMemSz uint32
|
|
|
|
// DefaultBridges specifies default number of bridges for the VM.
|
|
// Bridges can be used to hot plug devices
|
|
DefaultBridges uint32
|
|
|
|
// DisableBlockDeviceUse disallows a block device from being used.
|
|
DisableBlockDeviceUse bool
|
|
|
|
// Debug changes the default hypervisor and kernel parameters to
|
|
// enable debug output where available.
|
|
Debug bool
|
|
|
|
// MemPrealloc specifies if the memory should be pre-allocated
|
|
MemPrealloc bool
|
|
|
|
// HugePages specifies if the memory should be pre-allocated from huge pages
|
|
HugePages bool
|
|
|
|
// Realtime Used to enable/disable realtime
|
|
Realtime bool
|
|
|
|
// Mlock is used to control memory locking when Realtime is enabled
|
|
// Realtime=true and Mlock=false, allows for swapping out of VM memory
|
|
// enabling higher density
|
|
Mlock bool
|
|
|
|
// DisableNestingChecks is used to override customizations performed
|
|
// when running on top of another VMM.
|
|
DisableNestingChecks bool
|
|
}
|
|
|
|
func (conf *HypervisorConfig) valid() (bool, error) {
|
|
if conf.KernelPath == "" {
|
|
return false, fmt.Errorf("Missing kernel path")
|
|
}
|
|
|
|
if conf.ImagePath == "" {
|
|
return false, fmt.Errorf("Missing image path")
|
|
}
|
|
|
|
if conf.DefaultVCPUs == 0 {
|
|
conf.DefaultVCPUs = defaultVCPUs
|
|
}
|
|
|
|
if conf.DefaultMemSz == 0 {
|
|
conf.DefaultMemSz = defaultMemSzMiB
|
|
}
|
|
|
|
if conf.DefaultBridges == 0 {
|
|
conf.DefaultBridges = defaultBridges
|
|
}
|
|
|
|
if conf.BlockDeviceDriver == "" {
|
|
conf.BlockDeviceDriver = defaultBlockDriver
|
|
}
|
|
|
|
if conf.DefaultMaxVCPUs == 0 {
|
|
conf.DefaultMaxVCPUs = defaultMaxQemuVCPUs
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// AddKernelParam allows the addition of new kernel parameters to an existing
|
|
// hypervisor configuration.
|
|
func (conf *HypervisorConfig) AddKernelParam(p Param) error {
|
|
if p.Key == "" {
|
|
return fmt.Errorf("Empty kernel parameter")
|
|
}
|
|
|
|
conf.KernelParams = append(conf.KernelParams, p)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (conf *HypervisorConfig) addCustomAsset(a *asset) error {
|
|
if a == nil || a.path == "" {
|
|
// We did not get a custom asset, we will use the default one.
|
|
return nil
|
|
}
|
|
|
|
if !a.valid() {
|
|
return fmt.Errorf("Invalid %s at %s", a.kind, a.path)
|
|
}
|
|
|
|
virtLog.Debugf("Using custom %v asset %s", a.kind, a.path)
|
|
|
|
if conf.customAssets == nil {
|
|
conf.customAssets = make(map[assetType]*asset)
|
|
}
|
|
|
|
conf.customAssets[a.kind] = a
|
|
|
|
return nil
|
|
}
|
|
|
|
func (conf *HypervisorConfig) assetPath(t assetType) (string, error) {
|
|
// Custom assets take precedence over the configured ones
|
|
a, ok := conf.customAssets[t]
|
|
if ok {
|
|
return a.path, nil
|
|
}
|
|
|
|
// We could not find a custom asset for the given type, let's
|
|
// fall back to the configured ones.
|
|
switch t {
|
|
case kernelAsset:
|
|
return conf.KernelPath, nil
|
|
case imageAsset:
|
|
return conf.ImagePath, nil
|
|
case hypervisorAsset:
|
|
return conf.HypervisorPath, nil
|
|
case firmwareAsset:
|
|
return conf.FirmwarePath, nil
|
|
default:
|
|
return "", fmt.Errorf("Unknown asset type %v", t)
|
|
}
|
|
}
|
|
|
|
func (conf *HypervisorConfig) isCustomAsset(t assetType) bool {
|
|
_, ok := conf.customAssets[t]
|
|
if ok {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// KernelAssetPath returns the guest kernel path
|
|
func (conf *HypervisorConfig) KernelAssetPath() (string, error) {
|
|
return conf.assetPath(kernelAsset)
|
|
}
|
|
|
|
// CustomKernelAsset returns true if the kernel asset is a custom one, false otherwise.
|
|
func (conf *HypervisorConfig) CustomKernelAsset() bool {
|
|
return conf.isCustomAsset(kernelAsset)
|
|
}
|
|
|
|
// ImageAssetPath returns the guest image path
|
|
func (conf *HypervisorConfig) ImageAssetPath() (string, error) {
|
|
return conf.assetPath(imageAsset)
|
|
}
|
|
|
|
// CustomImageAsset returns true if the image asset is a custom one, false otherwise.
|
|
func (conf *HypervisorConfig) CustomImageAsset() bool {
|
|
return conf.isCustomAsset(imageAsset)
|
|
}
|
|
|
|
// HypervisorAssetPath returns the VM hypervisor path
|
|
func (conf *HypervisorConfig) HypervisorAssetPath() (string, error) {
|
|
return conf.assetPath(hypervisorAsset)
|
|
}
|
|
|
|
// CustomHypervisorAsset returns true if the hypervisor asset is a custom one, false otherwise.
|
|
func (conf *HypervisorConfig) CustomHypervisorAsset() bool {
|
|
return conf.isCustomAsset(hypervisorAsset)
|
|
}
|
|
|
|
// FirmwareAssetPath returns the guest firmware path
|
|
func (conf *HypervisorConfig) FirmwareAssetPath() (string, error) {
|
|
return conf.assetPath(firmwareAsset)
|
|
}
|
|
|
|
// CustomFirmwareAsset returns true if the firmware asset is a custom one, false otherwise.
|
|
func (conf *HypervisorConfig) CustomFirmwareAsset() bool {
|
|
return conf.isCustomAsset(firmwareAsset)
|
|
}
|
|
|
|
func appendParam(params []Param, parameter string, value string) []Param {
|
|
return append(params, Param{parameter, value})
|
|
}
|
|
|
|
// SerializeParams converts []Param to []string
|
|
func SerializeParams(params []Param, delim string) []string {
|
|
var parameters []string
|
|
|
|
for _, p := range params {
|
|
if p.Key == "" && p.Value == "" {
|
|
continue
|
|
} else if p.Key == "" {
|
|
parameters = append(parameters, fmt.Sprintf("%s", p.Value))
|
|
} else if p.Value == "" {
|
|
parameters = append(parameters, fmt.Sprintf("%s", p.Key))
|
|
} else if delim == "" {
|
|
parameters = append(parameters, fmt.Sprintf("%s", p.Key))
|
|
parameters = append(parameters, fmt.Sprintf("%s", p.Value))
|
|
} else {
|
|
parameters = append(parameters, fmt.Sprintf("%s%s%s", p.Key, delim, p.Value))
|
|
}
|
|
}
|
|
|
|
return parameters
|
|
}
|
|
|
|
// DeserializeParams converts []string to []Param
|
|
func DeserializeParams(parameters []string) []Param {
|
|
var params []Param
|
|
|
|
for _, param := range parameters {
|
|
if param == "" {
|
|
continue
|
|
}
|
|
p := strings.SplitN(param, "=", 2)
|
|
if len(p) == 2 {
|
|
params = append(params, Param{Key: p[0], Value: p[1]})
|
|
} else {
|
|
params = append(params, Param{Key: p[0], Value: ""})
|
|
}
|
|
}
|
|
|
|
return params
|
|
}
|
|
|
|
func getHostMemorySizeKb(memInfoPath string) (uint64, error) {
|
|
f, err := os.Open(memInfoPath)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
// Expected format: ["MemTotal:", "1234", "kB"]
|
|
parts := strings.Fields(scanner.Text())
|
|
|
|
// Sanity checks: Skip malformed entries.
|
|
if len(parts) < 3 || parts[0] != "MemTotal:" || parts[2] != "kB" {
|
|
continue
|
|
}
|
|
|
|
sizeKb, err := strconv.ParseUint(parts[1], 0, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
return sizeKb, nil
|
|
}
|
|
|
|
// Handle errors that may have occurred during the reading of the file.
|
|
if err := scanner.Err(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, fmt.Errorf("unable get MemTotal from %s", memInfoPath)
|
|
}
|
|
|
|
// RunningOnVMM checks if the system is running inside a VM.
|
|
func RunningOnVMM(cpuInfoPath string) (bool, error) {
|
|
if runtime.GOARCH == "arm64" {
|
|
virtLog.Debugf("Unable to know if the system is running inside a VM")
|
|
return false, nil
|
|
}
|
|
|
|
flagsField := "flags"
|
|
|
|
f, err := os.Open(cpuInfoPath)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
// Expected format: ["flags", ":", ...] or ["flags:", ...]
|
|
fields := strings.Fields(scanner.Text())
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
|
|
if !strings.HasPrefix(fields[0], flagsField) {
|
|
continue
|
|
}
|
|
|
|
for _, field := range fields[1:] {
|
|
if field == "hypervisor" {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
// As long as we have been able to analyze the fields from
|
|
// "flags", there is no reason to check what comes next from
|
|
// /proc/cpuinfo, because we already know we are not running
|
|
// on a VMM.
|
|
return false, nil
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return false, fmt.Errorf("Couldn't find %q from %q output", flagsField, cpuInfoPath)
|
|
}
|
|
|
|
// hypervisor is the virtcontainers hypervisor interface.
|
|
// The default hypervisor implementation is Qemu.
|
|
type hypervisor interface {
|
|
init(pod *Pod) error
|
|
createPod(podConfig PodConfig) error
|
|
startPod() error
|
|
waitPod(timeout int) error
|
|
stopPod() error
|
|
pausePod() error
|
|
resumePod() error
|
|
addDevice(devInfo interface{}, devType deviceType) error
|
|
hotplugAddDevice(devInfo interface{}, devType deviceType) error
|
|
hotplugRemoveDevice(devInfo interface{}, devType deviceType) error
|
|
getPodConsole(podID string) string
|
|
capabilities() capabilities
|
|
}
|