mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-29 16:57:18 +00:00
virtcontainers: add a vm abstraction layer
As representation of a guest without actual sandbox attached to it. This prepares for vm factory support. Signed-off-by: Peng Tao <bergwolf@gmail.com>
This commit is contained in:
parent
28b6104710
commit
8dda2dd7a5
227
virtcontainers/vm.go
Normal file
227
virtcontainers/vm.go
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2018 HyperHQ Inc.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package virtcontainers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/kata-containers/runtime/virtcontainers/pkg/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// VM is abstraction of a virtual machine.
|
||||
type VM struct {
|
||||
id string
|
||||
|
||||
hypervisor hypervisor
|
||||
agent agent
|
||||
|
||||
cpu uint32
|
||||
memory uint32
|
||||
|
||||
cpuDelta uint32
|
||||
}
|
||||
|
||||
// VMConfig is a collection of all info that a new blackbox VM needs.
|
||||
type VMConfig struct {
|
||||
HypervisorType HypervisorType
|
||||
HypervisorConfig HypervisorConfig
|
||||
|
||||
AgentType AgentType
|
||||
AgentConfig interface{}
|
||||
}
|
||||
|
||||
// Valid check VMConfig validity.
|
||||
func (c *VMConfig) Valid() error {
|
||||
return c.HypervisorConfig.valid()
|
||||
}
|
||||
|
||||
// NewVM creates a new VM based on provided VMConfig.
|
||||
func NewVM(config VMConfig) (*VM, error) {
|
||||
hypervisor, err := newHypervisor(config.HypervisorType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = config.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id := uuid.Generate().String()
|
||||
|
||||
virtLog.WithField("vm id", id).WithField("config", config).Info("create new vm")
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
virtLog.WithField("vm id", id).WithError(err).Error("failed to create new vm")
|
||||
}
|
||||
}()
|
||||
|
||||
if err = hypervisor.init(id, &config.HypervisorConfig, Resources{}, &filesystem{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = hypervisor.createSandbox(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agent := newAgent(config.AgentType)
|
||||
agentConfig := newAgentConfig(config.AgentType, config.AgentConfig)
|
||||
// do not keep connection for temp agent
|
||||
if c, ok := agentConfig.(KataAgentConfig); ok {
|
||||
c.LongLiveConn = false
|
||||
}
|
||||
vmSharePath := buildVMSharePath(id)
|
||||
err = agent.configure(hypervisor, id, vmSharePath, true, agentConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = hypervisor.startSandbox(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
virtLog.WithField("vm id", id).WithError(err).Info("clean up vm")
|
||||
hypervisor.stopSandbox()
|
||||
}
|
||||
}()
|
||||
|
||||
// VMs booted from template are paused, do not check
|
||||
if !config.HypervisorConfig.BootFromTemplate {
|
||||
err = hypervisor.waitSandbox(vmStartTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
virtLog.WithField("vm id", id).Info("check agent status")
|
||||
err = agent.check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &VM{
|
||||
id: id,
|
||||
hypervisor: hypervisor,
|
||||
agent: agent,
|
||||
cpu: config.HypervisorConfig.DefaultVCPUs,
|
||||
memory: config.HypervisorConfig.DefaultMemSz,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildVMSharePath(id string) string {
|
||||
return filepath.Join(RunVMStoragePath, id, "shared")
|
||||
}
|
||||
|
||||
func (v *VM) logger() logrus.FieldLogger {
|
||||
return virtLog.WithField("vm id", v.id)
|
||||
}
|
||||
|
||||
// Pause pauses a VM.
|
||||
func (v *VM) Pause() error {
|
||||
v.logger().Info("pause vm")
|
||||
return v.hypervisor.pauseSandbox()
|
||||
}
|
||||
|
||||
// Save saves a VM to persistent disk.
|
||||
func (v *VM) Save() error {
|
||||
v.logger().Info("save vm")
|
||||
return v.hypervisor.saveSandbox()
|
||||
}
|
||||
|
||||
// Resume resumes a paused VM.
|
||||
func (v *VM) Resume() error {
|
||||
v.logger().Info("resume vm")
|
||||
return v.hypervisor.resumeSandbox()
|
||||
}
|
||||
|
||||
// Start kicks off a configured VM.
|
||||
func (v *VM) Start() error {
|
||||
v.logger().Info("start vm")
|
||||
return v.hypervisor.startSandbox()
|
||||
}
|
||||
|
||||
// Stop stops a VM process.
|
||||
func (v *VM) Stop() error {
|
||||
v.logger().Info("kill vm")
|
||||
return v.hypervisor.stopSandbox()
|
||||
}
|
||||
|
||||
// AddCPUs adds num of CPUs to the VM.
|
||||
func (v *VM) AddCPUs(num uint32) error {
|
||||
if num > 0 {
|
||||
v.logger().Infof("hot adding %d vCPUs", num)
|
||||
if _, err := v.hypervisor.hotplugAddDevice(num, cpuDev); err != nil {
|
||||
return err
|
||||
}
|
||||
v.cpuDelta += num
|
||||
v.cpu += num
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddMemory adds numMB of memory to the VM.
|
||||
func (v *VM) AddMemory(numMB uint32) error {
|
||||
if numMB > 0 {
|
||||
v.logger().Infof("hot adding %d MB memory", numMB)
|
||||
dev := &memoryDevice{1, int(numMB)}
|
||||
if _, err := v.hypervisor.hotplugAddDevice(dev, memoryDev); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnlineCPUMemory puts the hotplugged CPU and memory online.
|
||||
func (v *VM) OnlineCPUMemory() error {
|
||||
v.logger().Infof("online CPU %d and memory", v.cpuDelta)
|
||||
err := v.agent.onlineCPUMem(v.cpuDelta)
|
||||
if err == nil {
|
||||
v.cpuDelta = 0
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *VM) assignSandbox(s *Sandbox) error {
|
||||
// add vm symlinks
|
||||
// - link vm socket from sandbox dir (/run/vc/vm/sbid/<kata.sock>) to vm dir (/run/vc/vm/vmid/<kata.sock>)
|
||||
// - link 9pfs share path from sandbox dir (/run/kata-containers/shared/sandboxes/sbid/) to vm dir (/run/vc/vm/vmid/shared/)
|
||||
|
||||
vmSharePath := buildVMSharePath(v.id)
|
||||
vmSockDir := v.agent.getVMPath(v.id)
|
||||
sbSharePath := s.agent.getSharePath(s.id)
|
||||
sbSockDir := s.agent.getVMPath(s.id)
|
||||
|
||||
v.logger().WithFields(logrus.Fields{
|
||||
"vmSharePath": vmSharePath,
|
||||
"vmSockDir": vmSockDir,
|
||||
"sbSharePath": sbSharePath,
|
||||
"sbSockDir": sbSockDir,
|
||||
}).Infof("assign vm to sandbox %s", s.id)
|
||||
|
||||
// First make sure the symlinks do not exist
|
||||
os.RemoveAll(sbSharePath)
|
||||
os.RemoveAll(sbSockDir)
|
||||
|
||||
if err := os.Symlink(vmSharePath, sbSharePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Symlink(vmSockDir, sbSockDir); err != nil {
|
||||
os.Remove(sbSharePath)
|
||||
return err
|
||||
}
|
||||
|
||||
s.hypervisor = v.hypervisor
|
||||
|
||||
return nil
|
||||
}
|
83
virtcontainers/vm_test.go
Normal file
83
virtcontainers/vm_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2018 HyperHQ Inc.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package virtcontainers
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewVM(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testDir, _ := ioutil.TempDir("", "vmfactory-tmp-")
|
||||
config := VMConfig{
|
||||
HypervisorType: MockHypervisor,
|
||||
AgentType: NoopAgentType,
|
||||
}
|
||||
hyperConfig := HypervisorConfig{
|
||||
KernelPath: testDir,
|
||||
ImagePath: testDir,
|
||||
}
|
||||
|
||||
var vm *VM
|
||||
_, err := NewVM(config)
|
||||
assert.Error(err)
|
||||
|
||||
config.HypervisorConfig = hyperConfig
|
||||
vm, err = NewVM(config)
|
||||
assert.Nil(err)
|
||||
|
||||
// VM operations
|
||||
err = vm.Pause()
|
||||
assert.Nil(err)
|
||||
err = vm.Resume()
|
||||
assert.Nil(err)
|
||||
err = vm.Start()
|
||||
assert.Nil(err)
|
||||
err = vm.Save()
|
||||
assert.Nil(err)
|
||||
err = vm.Stop()
|
||||
assert.Nil(err)
|
||||
err = vm.AddCPUs(2)
|
||||
assert.Nil(err)
|
||||
err = vm.AddMemory(128)
|
||||
assert.Nil(err)
|
||||
err = vm.OnlineCPUMemory()
|
||||
assert.Nil(err)
|
||||
|
||||
// template VM
|
||||
config.HypervisorConfig.BootFromTemplate = true
|
||||
_, err = NewVM(config)
|
||||
assert.Error(err)
|
||||
|
||||
config.HypervisorConfig.MemoryPath = testDir
|
||||
_, err = NewVM(config)
|
||||
assert.Error(err)
|
||||
|
||||
config.HypervisorConfig.DevicesStatePath = testDir
|
||||
_, err = NewVM(config)
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestVMConfigValid(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := VMConfig{}
|
||||
|
||||
err := config.Valid()
|
||||
assert.Error(err)
|
||||
|
||||
testDir, _ := ioutil.TempDir("", "vmfactory-tmp-")
|
||||
config.HypervisorConfig = HypervisorConfig{
|
||||
KernelPath: testDir,
|
||||
InitrdPath: testDir,
|
||||
}
|
||||
err = config.Valid()
|
||||
assert.Nil(err)
|
||||
}
|
Loading…
Reference in New Issue
Block a user