mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-26 15:32:30 +00:00
clh: use http client
Instead of build a command, use Cloud Hypervisor http API. Fixes: #2165 Signed-off-by: Bo Chen <chen.bo@intel.com> Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
This commit is contained in:
parent
60102188cd
commit
f73723a23f
@ -62,6 +62,9 @@ default_memory = @DEFMEMSZ@
|
|||||||
# Path to vhost-user-fs daemon.
|
# Path to vhost-user-fs daemon.
|
||||||
virtio_fs_daemon = "@DEFVIRTIOFSDAEMON@"
|
virtio_fs_daemon = "@DEFVIRTIOFSDAEMON@"
|
||||||
|
|
||||||
|
# Default size of DAX cache in MiB
|
||||||
|
virtio_fs_cache_size = @DEFVIRTIOFSCACHESIZE@
|
||||||
|
|
||||||
# cloud-hypervisor prefers virtiofs caching (dax) for performance reasons
|
# cloud-hypervisor prefers virtiofs caching (dax) for performance reasons
|
||||||
virtio_fs_cache = "always"
|
virtio_fs_cache = "always"
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ virtio_fs_cache = "always"
|
|||||||
# to the proxy logs, but only when proxy debug is also enabled.
|
# to the proxy logs, but only when proxy debug is also enabled.
|
||||||
#
|
#
|
||||||
# Default false
|
# Default false
|
||||||
# enable_debug = true
|
#enable_debug = true
|
||||||
|
|
||||||
[proxy.@PROJECT_TYPE@]
|
[proxy.@PROJECT_TYPE@]
|
||||||
path = "@PROXYPATH@"
|
path = "@PROXYPATH@"
|
||||||
|
@ -9,8 +9,10 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -20,6 +22,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
|
persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
|
||||||
|
chclient "github.com/kata-containers/runtime/virtcontainers/pkg/cloud-hypervisor/client"
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -42,7 +45,14 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Values are mandatory by http API
|
||||||
|
// Values based on:
|
||||||
|
// github.com/cloud-hypervisor/cloud-hypervisor/blob/v0.3.0/vmm/src/config.rs#L395
|
||||||
|
clhFsQueues = 1
|
||||||
|
clhFsQueueSize = 1024
|
||||||
clhTimeout = 10
|
clhTimeout = 10
|
||||||
|
clhAPITimeout = 1
|
||||||
|
clhStopSandboxTimeout = 3
|
||||||
clhSocket = "clh.sock"
|
clhSocket = "clh.sock"
|
||||||
clhAPISocket = "clh-api.sock"
|
clhAPISocket = "clh-api.sock"
|
||||||
clhLogFile = "clh.log"
|
clhLogFile = "clh.log"
|
||||||
@ -68,6 +78,7 @@ type CloudHypervisorState struct {
|
|||||||
state clhState
|
state clhState
|
||||||
PID int
|
PID int
|
||||||
VirtiofsdPID int
|
VirtiofsdPID int
|
||||||
|
apiSocket string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CloudHypervisorState) reset() {
|
func (s *CloudHypervisorState) reset() {
|
||||||
@ -77,15 +88,15 @@ func (s *CloudHypervisorState) reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type cloudHypervisor struct {
|
type cloudHypervisor struct {
|
||||||
id string
|
id string
|
||||||
state CloudHypervisorState
|
state CloudHypervisorState
|
||||||
store *store.VCStore
|
store *store.VCStore
|
||||||
config HypervisorConfig
|
config HypervisorConfig
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
socketPath string
|
APIClient *chclient.DefaultApiService
|
||||||
version CloudHypervisorVersion
|
version CloudHypervisorVersion
|
||||||
cliBuilder *DefaultCLIBuilder
|
vmconfig chclient.VmConfig
|
||||||
cmdOutput bytes.Buffer
|
cmdOutput bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
var clhKernelParams = []Param{
|
var clhKernelParams = []Param{
|
||||||
@ -111,6 +122,8 @@ var clhDebugKernelParams = []Param{
|
|||||||
//
|
//
|
||||||
//###########################################################
|
//###########################################################
|
||||||
|
|
||||||
|
// For cloudHypervisor this call only sets the internal structure up.
|
||||||
|
// The VM will be created and started through startSandbox().
|
||||||
func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networkNS NetworkNamespace, hypervisorConfig *HypervisorConfig, vcStore *store.VCStore, stateful bool) error {
|
func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networkNS NetworkNamespace, hypervisorConfig *HypervisorConfig, vcStore *store.VCStore, stateful bool) error {
|
||||||
clh.ctx = ctx
|
clh.ctx = ctx
|
||||||
|
|
||||||
@ -131,11 +144,13 @@ func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networ
|
|||||||
clhPath, perr := clh.clhPath()
|
clhPath, perr := clh.clhPath()
|
||||||
if perr != nil {
|
if perr != nil {
|
||||||
return perr
|
return perr
|
||||||
|
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(clhPath, "cloud-hypervisor") {
|
if strings.HasSuffix(clhPath, "cloud-hypervisor") {
|
||||||
err = clh.getAvailableVersion()
|
err = clh.getAvailableVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if clh.version.Major < supportedMajorVersion && clh.version.Minor < supportedMinorVersion {
|
if clh.version.Major < supportedMajorVersion && clh.version.Minor < supportedMinorVersion {
|
||||||
@ -146,15 +161,8 @@ func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networ
|
|||||||
supportedMinorVersion)
|
supportedMinorVersion)
|
||||||
return errors.New(errorMessage)
|
return errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
clh.cliBuilder = &DefaultCLIBuilder{}
|
|
||||||
|
|
||||||
socketPath, err := clh.vsockSocketPath(id)
|
|
||||||
if err != nil {
|
|
||||||
clh.Logger().Info("Invalid socket path for cloud-hypervisor")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
clh.socketPath = socketPath
|
|
||||||
|
|
||||||
clh.Logger().WithField("function", "createSandbox").Info("creating Sandbox")
|
clh.Logger().WithField("function", "createSandbox").Info("creating Sandbox")
|
||||||
|
|
||||||
@ -164,41 +172,41 @@ func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networ
|
|||||||
clh.Logger().WithField("function", "createSandbox").WithError(err).Info("No info could be fetched")
|
clh.Logger().WithField("function", "createSandbox").WithError(err).Info("No info could be fetched")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set initial memomory size of the cloud hypervisor
|
// Set initial memomory size of the virtual machine
|
||||||
clh.cliBuilder.SetMemory(&CLIMemory{
|
clh.vmconfig.Memory.Size = int64(clh.config.MemorySize) << utils.MibToBytesShift
|
||||||
memorySize: clh.config.MemorySize,
|
clh.vmconfig.Memory.File = "/dev/shm"
|
||||||
backingFile: "/dev/shm",
|
// Set initial amount of cpu's for the virtual machine
|
||||||
})
|
clh.vmconfig.Cpus = chclient.CpuConfig{
|
||||||
// Set initial amount of cpu's for the cloud hypervisor
|
// cast to int32, as openAPI has a limitation that it does not support unsigned values
|
||||||
clh.cliBuilder.SetCpus(&CLICpus{
|
CpuCount: int32(clh.config.NumVCPUs),
|
||||||
cpus: clh.config.NumVCPUs,
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// Add the kernel path
|
// Add the kernel path
|
||||||
kernelPath, err := clh.config.KernelAssetPath()
|
kernelPath, err := clh.config.KernelAssetPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
clh.cliBuilder.SetKernel(&CLIKernel{
|
clh.vmconfig.Kernel = chclient.KernelConfig{
|
||||||
path: kernelPath,
|
Path: kernelPath,
|
||||||
})
|
}
|
||||||
|
|
||||||
// First take the default parameters defined by this driver
|
// First take the default parameters defined by this driver
|
||||||
clh.cliBuilder.AddKernelParameters(clhKernelParams)
|
params := clhKernelParams
|
||||||
|
|
||||||
// Followed by extra debug parameters if debug enabled in configuration file
|
// Followed by extra debug parameters if debug enabled in configuration file
|
||||||
if clh.config.Debug {
|
if clh.config.Debug {
|
||||||
clh.cliBuilder.AddKernelParameters(clhDebugKernelParams)
|
params = append(params, clhDebugKernelParams...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Followed by extra debug parameters defined in the configuration file
|
// Followed by extra debug parameters defined in the configuration file
|
||||||
clh.cliBuilder.AddKernelParameters(clh.config.KernelParams)
|
params = append(params, clh.config.KernelParams...)
|
||||||
|
|
||||||
|
clh.vmconfig.Cmdline.Args = kernelParamsToString(params)
|
||||||
|
|
||||||
// set random device generator to hypervisor
|
// set random device generator to hypervisor
|
||||||
clh.cliBuilder.SetRng(&CLIRng{
|
clh.vmconfig.Rng = chclient.RngConfig{
|
||||||
src: clh.config.EntropySource,
|
Src: clh.config.EntropySource,
|
||||||
iommu: false,
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// set the initial root/boot disk of hypervisor
|
// set the initial root/boot disk of hypervisor
|
||||||
imagePath, err := clh.config.ImageAssetPath()
|
imagePath, err := clh.config.ImageAssetPath()
|
||||||
@ -206,34 +214,14 @@ func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networ
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if imagePath != "" {
|
if imagePath == "" {
|
||||||
clh.cliBuilder.SetDisk(&CLIDisk{
|
return errors.New("image path is empty")
|
||||||
path: imagePath,
|
|
||||||
iommu: false,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the virtio-fs to the hypervisor
|
disk := chclient.DiskConfig{
|
||||||
vfsdSockPath, err := clh.virtioFsSocketPath(clh.id)
|
Path: imagePath,
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if clh.config.VirtioFSCache == virtioFsCacheAlways {
|
|
||||||
clh.cliBuilder.SetFs(&CLIFs{
|
|
||||||
tag: "kataShared",
|
|
||||||
socketPath: vfsdSockPath,
|
|
||||||
queues: 1,
|
|
||||||
queueSize: 512,
|
|
||||||
dax: true,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
clh.cliBuilder.SetFs(&CLIFs{
|
|
||||||
tag: "kataShared",
|
|
||||||
socketPath: vfsdSockPath,
|
|
||||||
queues: 1,
|
|
||||||
queueSize: 512,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
clh.vmconfig.Disks = append(clh.vmconfig.Disks, disk)
|
||||||
|
|
||||||
// set the serial console to the cloud hypervisor
|
// set the serial console to the cloud hypervisor
|
||||||
if clh.config.Debug {
|
if clh.config.Debug {
|
||||||
@ -241,41 +229,40 @@ func (clh *cloudHypervisor) createSandbox(ctx context.Context, id string, networ
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
clh.cliBuilder.SetSerial(&CLISerialConsole{
|
clh.vmconfig.Serial = chclient.ConsoleConfig{
|
||||||
consoleType: cctFILE,
|
Mode: cctFILE,
|
||||||
filePath: serialPath,
|
File: serialPath,
|
||||||
})
|
}
|
||||||
logFilePath, err := clh.logFilePath(clh.id)
|
|
||||||
if err != nil {
|
} else {
|
||||||
return err
|
clh.vmconfig.Serial = chclient.ConsoleConfig{
|
||||||
|
Mode: cctOFF,
|
||||||
}
|
}
|
||||||
clh.cliBuilder.SetLogFile(&CLILogFile{
|
|
||||||
path: logFilePath,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clh.cliBuilder.SetConsole(&CLIConsole{
|
clh.vmconfig.Console = chclient.ConsoleConfig{
|
||||||
consoleType: cctOFF,
|
Mode: cctOFF,
|
||||||
})
|
}
|
||||||
|
|
||||||
// Move the API endpoint socket location for the
|
// Overwrite the default value of HTTP API socket path for cloud hypervisor
|
||||||
// by default enabled api endpoint
|
|
||||||
apiSocketPath, err := clh.apiSocketPath(id)
|
apiSocketPath, err := clh.apiSocketPath(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
clh.Logger().Info("Invalid api socket path for cloud-hypervisor")
|
clh.Logger().Info("Invalid api socket path for cloud-hypervisor")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
clh.cliBuilder.SetAPISocket(&CLIAPISocket{
|
clh.state.apiSocket = apiSocketPath
|
||||||
socketPath: apiSocketPath,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startSandbox will start the VMM and boot the virtual machine for the given sandbox.
|
||||||
func (clh *cloudHypervisor) startSandbox(timeout int) error {
|
func (clh *cloudHypervisor) startSandbox(timeout int) error {
|
||||||
span, _ := clh.trace("startSandbox")
|
span, _ := clh.trace("startSandbox")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), clhAPITimeout*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
clh.Logger().WithField("function", "startSandbox").Info("starting Sandbox")
|
clh.Logger().WithField("function", "startSandbox").Info("starting Sandbox")
|
||||||
|
|
||||||
vmPath := filepath.Join(store.RunVMStoragePath(), clh.id)
|
vmPath := filepath.Join(store.RunVMStoragePath(), clh.id)
|
||||||
@ -302,19 +289,26 @@ func (clh *cloudHypervisor) startSandbox(timeout int) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to launch cloud-hypervisor: %s, error messages from log: %s", err, strErr)
|
return fmt.Errorf("failed to launch cloud-hypervisor: %s, error messages from log: %s", err, strErr)
|
||||||
}
|
}
|
||||||
|
clh.state.PID = pid
|
||||||
|
|
||||||
if err := clh.waitVMM(clhTimeout); err != nil {
|
if err := clh.waitVMM(clhTimeout); err != nil {
|
||||||
clh.Logger().WithField("error", err).WithField("output", clh.cmdOutput.String()).Warn("cloud-hypervisor init failed")
|
clh.Logger().WithField("error", err).WithField("output", clh.cmdOutput.String()).Warn("cloud-hypervisor init failed")
|
||||||
clh.shutdownVirtiofsd()
|
clh.shutdownVirtiofsd()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
clh.state.PID = pid
|
if err := clh.bootVM(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
clh.state.state = clhReady
|
clh.state.state = clhReady
|
||||||
clh.storeState()
|
clh.storeState()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSandboxConsole builds the path of the console where we can read
|
||||||
|
// logs coming from the sandbox.
|
||||||
func (clh *cloudHypervisor) getSandboxConsole(id string) (string, error) {
|
func (clh *cloudHypervisor) getSandboxConsole(id string) (string, error) {
|
||||||
clh.Logger().WithField("function", "getSandboxConsole").WithField("id", id).Info("Get Sandbox Console")
|
clh.Logger().WithField("function", "getSandboxConsole").WithField("id", id).Info("Get Sandbox Console")
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -408,7 +402,7 @@ func (clh *cloudHypervisor) load(s persistapi.HypervisorState) {
|
|||||||
|
|
||||||
func (clh *cloudHypervisor) check() error {
|
func (clh *cloudHypervisor) check() error {
|
||||||
cl := clh.client()
|
cl := clh.client()
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), clhApiTimeout*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), clhAPITimeout*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
_, _, err := cl.VmmPingGet(ctx)
|
_, _, err := cl.VmmPingGet(ctx)
|
||||||
@ -431,25 +425,14 @@ func (clh *cloudHypervisor) addDevice(devInfo interface{}, devType deviceType) e
|
|||||||
|
|
||||||
switch v := devInfo.(type) {
|
switch v := devInfo.(type) {
|
||||||
case Endpoint:
|
case Endpoint:
|
||||||
clh.Logger().WithField("function", "addDevice").Infof("Adding Endpoint of type %v", v)
|
clh.addNet(v)
|
||||||
clh.cliBuilder.AddNet(CLINet{
|
|
||||||
device: v.Name(),
|
|
||||||
mac: v.HardwareAddr(),
|
|
||||||
})
|
|
||||||
case types.HybridVSock:
|
case types.HybridVSock:
|
||||||
clh.Logger().WithFields(log.Fields{
|
clh.addVSock(defaultGuestVSockCID, v.UdsPath)
|
||||||
"function": "addDevice",
|
case types.Volume:
|
||||||
"path": v.UdsPath,
|
err = clh.addVolume(v)
|
||||||
"cid": v.ContextID,
|
|
||||||
"port": v.Port,
|
|
||||||
}).Info("Adding HybridVSock")
|
|
||||||
clh.cliBuilder.SetVsock(&CLIVsock{
|
|
||||||
cid: uint32(v.ContextID),
|
|
||||||
socketPath: v.UdsPath,
|
|
||||||
iommu: false,
|
|
||||||
})
|
|
||||||
default:
|
default:
|
||||||
clh.Logger().WithField("function", "addDevice").Warnf("Add device of type %v is not supported.", v)
|
clh.Logger().WithField("function", "addDevice").Warnf("Add device of type %v is not supported.", v)
|
||||||
|
return fmt.Errorf("Not implemented support for %s", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -465,6 +448,7 @@ func (clh *cloudHypervisor) Logger() *log.Entry {
|
|||||||
return virtLog.WithField("subsystem", "cloudHypervisor")
|
return virtLog.WithField("subsystem", "cloudHypervisor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds all capabilities supported by cloudHypervisor implementation of hypervisor interface
|
||||||
func (clh *cloudHypervisor) capabilities() types.Capabilities {
|
func (clh *cloudHypervisor) capabilities() types.Capabilities {
|
||||||
span, _ := clh.trace("capabilities")
|
span, _ := clh.trace("capabilities")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
@ -525,11 +509,20 @@ func (clh *cloudHypervisor) terminate() (err error) {
|
|||||||
}
|
}
|
||||||
clh.Logger().WithField("PID", pid).Info("Stopping Cloud Hypervisor")
|
clh.Logger().WithField("PID", pid).Info("Stopping Cloud Hypervisor")
|
||||||
|
|
||||||
// Send a SIGTERM to the VM process to try to stop it properly
|
clhRunning, err := clh.isClhRunning(clhStopSandboxTimeout)
|
||||||
if err = syscall.Kill(pid, syscall.SIGTERM); err != nil {
|
|
||||||
if err == syscall.ESRCH {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !clhRunning {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), clhStopSandboxTimeout*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if _, err = clh.client().ShutdownVMM(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,8 +533,8 @@ func (clh *cloudHypervisor) terminate() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(tInit).Seconds() >= fcStopSandboxTimeout {
|
if time.Since(tInit).Seconds() >= clhStopSandboxTimeout {
|
||||||
clh.Logger().Warnf("VM still running after waiting %ds", fcStopSandboxTimeout)
|
clh.Logger().Warnf("VM still running after waiting %ds", clhStopSandboxTimeout)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +566,6 @@ func (clh *cloudHypervisor) generateSocket(id string, useVsock bool) (interface{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
clh.socketPath = udsPath
|
|
||||||
return types.HybridVSock{
|
return types.HybridVSock{
|
||||||
UdsPath: udsPath,
|
UdsPath: udsPath,
|
||||||
ContextID: cid,
|
ContextID: cid,
|
||||||
@ -733,44 +725,19 @@ func (clh *cloudHypervisor) storeState() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clh *cloudHypervisor) waitVMM(timeout int) error {
|
func (clh *cloudHypervisor) waitVMM(timeout uint) error {
|
||||||
|
|
||||||
var err error
|
clhRunning, err := clh.isClhRunning(timeout)
|
||||||
timeoutDuration := time.Duration(timeout) * time.Second
|
|
||||||
|
|
||||||
sockReady := make(chan error, 1)
|
if err != nil {
|
||||||
go func() {
|
return err
|
||||||
udsPath, err := clh.vsockSocketPath(clh.id)
|
|
||||||
if err != nil {
|
|
||||||
sockReady <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
addr, err := net.ResolveUnixAddr("unix", udsPath)
|
|
||||||
if err != nil {
|
|
||||||
sockReady <- err
|
|
||||||
}
|
|
||||||
conn, err := net.DialUnix("unix", nil, addr)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
} else {
|
|
||||||
conn.Close()
|
|
||||||
sockReady <- nil
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err = <-sockReady:
|
|
||||||
case <-time.After(timeoutDuration):
|
|
||||||
err = fmt.Errorf("timed out waiting for cloud-hypervisor vsock")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1000 * time.Millisecond)
|
if !clhRunning {
|
||||||
return err
|
return fmt.Errorf("CLH is not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clh *cloudHypervisor) clhPath() (string, error) {
|
func (clh *cloudHypervisor) clhPath() (string, error) {
|
||||||
@ -795,6 +762,7 @@ func (clh *cloudHypervisor) getAvailableVersion() error {
|
|||||||
clhPath, err := clh.clhPath()
|
clhPath, err := clh.clhPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(clhPath, "--version")
|
cmd := exec.Command(clhPath, "--version")
|
||||||
@ -806,30 +774,37 @@ func (clh *cloudHypervisor) getAvailableVersion() error {
|
|||||||
words := strings.Fields(string(out))
|
words := strings.Fields(string(out))
|
||||||
if len(words) != 2 {
|
if len(words) != 2 {
|
||||||
return errors.New("Failed to parse cloud-hypervisor version response. Illegal length")
|
return errors.New("Failed to parse cloud-hypervisor version response. Illegal length")
|
||||||
|
|
||||||
}
|
}
|
||||||
versionSplit := strings.SplitN(words[1], ".", -1)
|
versionSplit := strings.SplitN(words[1], ".", -1)
|
||||||
if len(versionSplit) != 3 {
|
if len(versionSplit) != 3 {
|
||||||
return errors.New("Failed to parse cloud-hypervisor version field. Illegal length")
|
return errors.New("Failed to parse cloud-hypervisor version field. Illegal length")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
major, err := strconv.ParseUint(versionSplit[0], 10, 64)
|
major, err := strconv.ParseUint(versionSplit[0], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
minor, err := strconv.ParseUint(versionSplit[1], 10, 64)
|
minor, err := strconv.ParseUint(versionSplit[1], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
revision, err := strconv.ParseUint(versionSplit[2], 10, 64)
|
revision, err := strconv.ParseUint(versionSplit[2], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clh.version = CloudHypervisorVersion{
|
clh.version = CloudHypervisorVersion{
|
||||||
Major: int(major),
|
Major: int(major),
|
||||||
Minor: int(minor),
|
Minor: int(minor),
|
||||||
Revision: int(revision),
|
Revision: int(revision),
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clh *cloudHypervisor) LaunchClh() (string, int, error) {
|
func (clh *cloudHypervisor) LaunchClh() (string, int, error) {
|
||||||
@ -840,17 +815,22 @@ func (clh *cloudHypervisor) LaunchClh() (string, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", -1, err
|
return "", -1, err
|
||||||
}
|
}
|
||||||
director := &CommandLineDirector{}
|
|
||||||
|
|
||||||
cli, err := director.Build(clh.cliBuilder)
|
args := []string{cscApisocket, clh.state.apiSocket}
|
||||||
if err != nil {
|
if clh.config.Debug {
|
||||||
return "", -1, err
|
|
||||||
|
logfile, err := clh.logFilePath(clh.id)
|
||||||
|
if err != nil {
|
||||||
|
return "", -1, err
|
||||||
|
}
|
||||||
|
args = append(args, cscLogFile)
|
||||||
|
args = append(args, logfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
clh.Logger().WithField("path", clhPath).Info()
|
clh.Logger().WithField("path", clhPath).Info()
|
||||||
clh.Logger().WithField("args", strings.Join(cli.args, " ")).Info()
|
clh.Logger().WithField("args", strings.Join(args, " ")).Info()
|
||||||
|
|
||||||
cmd := exec.Command(clhPath, cli.args...)
|
cmd := exec.Command(clhPath, args...)
|
||||||
cmd.Stdout = &clh.cmdOutput
|
cmd.Stdout = &clh.cmdOutput
|
||||||
cmd.Stderr = &clh.cmdOutput
|
cmd.Stderr = &clh.cmdOutput
|
||||||
|
|
||||||
@ -882,10 +862,8 @@ func MaxClhVCPUs() uint32 {
|
|||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|
||||||
const (
|
const (
|
||||||
cctOFF string = "off"
|
cctOFF string = "Off"
|
||||||
cctFILE string = "file"
|
cctFILE string = "File"
|
||||||
cctNULL string = "null"
|
|
||||||
cctTTY string = "tty"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -1184,16 +1162,11 @@ func (o *CLILogFile) Build(cmdline *CommandLine) {
|
|||||||
//****************************************
|
//****************************************
|
||||||
// The kernel command line
|
// The kernel command line
|
||||||
//****************************************
|
//****************************************
|
||||||
type CLICmdline struct {
|
|
||||||
params []Param
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *CLICmdline) Build(cmdline *CommandLine) {
|
func kernelParamsToString(params []Param) string {
|
||||||
|
|
||||||
cmdline.args = append(cmdline.args, cscCmdline)
|
|
||||||
|
|
||||||
var paramBuilder strings.Builder
|
var paramBuilder strings.Builder
|
||||||
for _, p := range o.params {
|
for _, p := range params {
|
||||||
paramBuilder.WriteString(p.Key)
|
paramBuilder.WriteString(p.Key)
|
||||||
if len(p.Value) > 0 {
|
if len(p.Value) > 0 {
|
||||||
|
|
||||||
@ -1202,141 +1175,195 @@ func (o *CLICmdline) Build(cmdline *CommandLine) {
|
|||||||
}
|
}
|
||||||
paramBuilder.WriteString(" ")
|
paramBuilder.WriteString(" ")
|
||||||
}
|
}
|
||||||
cmdline.args = append(cmdline.args, strings.TrimSpace(paramBuilder.String()))
|
return strings.TrimSpace(paramBuilder.String())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************************
|
//****************************************
|
||||||
// The Default Builder
|
// API calls
|
||||||
//**********************************
|
//****************************************
|
||||||
type DefaultCLIBuilder struct {
|
func (clh *cloudHypervisor) isClhRunning(timeout uint) (bool, error) {
|
||||||
console *CLIConsole
|
|
||||||
serial *CLISerialConsole
|
|
||||||
apiSocket *CLIAPISocket
|
|
||||||
cpus *CLICpus
|
|
||||||
memory *CLIMemory
|
|
||||||
kernel *CLIKernel
|
|
||||||
disk *CLIDisk
|
|
||||||
fs *CLIFs
|
|
||||||
rng *CLIRng
|
|
||||||
logFile *CLILogFile
|
|
||||||
vsock *CLIVsock
|
|
||||||
cmdline *CLICmdline
|
|
||||||
nets *CLINets
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) AddKernelParameters(params []Param) {
|
pid := clh.state.PID
|
||||||
|
|
||||||
if d.cmdline == nil {
|
// Check if clh process is running, in case it is not, let's
|
||||||
d.cmdline = &CLICmdline{}
|
// return from here.
|
||||||
}
|
if err := syscall.Kill(pid, syscall.Signal(0)); err != nil {
|
||||||
d.cmdline.params = append(d.cmdline.params, params...)
|
return false, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetConsole(console *CLIConsole) {
|
|
||||||
d.console = console
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetCpus(cpus *CLICpus) {
|
|
||||||
d.cpus = cpus
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetDisk(disk *CLIDisk) {
|
|
||||||
d.disk = disk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetFs(fs *CLIFs) {
|
|
||||||
d.fs = fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetKernel(kernel *CLIKernel) {
|
|
||||||
d.kernel = kernel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetMemory(memory *CLIMemory) {
|
|
||||||
d.memory = memory
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) AddNet(net CLINet) {
|
|
||||||
if d.nets == nil {
|
|
||||||
d.nets = &CLINets{}
|
|
||||||
}
|
|
||||||
d.nets.networks = append(d.nets.networks, net)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetRng(rng *CLIRng) {
|
|
||||||
d.rng = rng
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetSerial(serial *CLISerialConsole) {
|
|
||||||
d.serial = serial
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetVsock(vsock *CLIVsock) {
|
|
||||||
d.vsock = vsock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetAPISocket(apiSocket *CLIAPISocket) {
|
|
||||||
d.apiSocket = apiSocket
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) SetLogFile(logFile *CLILogFile) {
|
|
||||||
d.logFile = logFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultCLIBuilder) GetCommandLine() (*CommandLine, error) {
|
|
||||||
|
|
||||||
cmdLine := &CommandLine{}
|
|
||||||
|
|
||||||
if d.serial != nil {
|
|
||||||
d.serial.Build(cmdLine)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.console != nil {
|
timeStart := time.Now()
|
||||||
d.console.Build(cmdLine)
|
cl := clh.client()
|
||||||
|
for {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), clhAPITimeout*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_, _, err := cl.VmmPingGet(ctx)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Since(timeStart).Seconds() > float64(timeout) {
|
||||||
|
return false, fmt.Errorf("Failed to connect to API (timeout %ds): %s", timeout, openAPIClientError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(10) * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.apiSocket != nil {
|
|
||||||
d.apiSocket.Build(cmdLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.logFile != nil {
|
|
||||||
d.logFile.Build(cmdLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.cpus != nil {
|
|
||||||
d.cpus.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.memory != nil {
|
|
||||||
d.memory.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.disk != nil {
|
|
||||||
d.disk.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.rng != nil {
|
|
||||||
d.rng.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.vsock != nil {
|
|
||||||
d.vsock.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.fs != nil {
|
|
||||||
d.fs.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.kernel != nil {
|
|
||||||
d.kernel.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.nets != nil {
|
|
||||||
d.nets.Build(cmdLine)
|
|
||||||
}
|
|
||||||
if d.cmdline != nil {
|
|
||||||
d.cmdline.Build(cmdLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmdLine, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandLineDirector struct{}
|
func (clh *cloudHypervisor) client() *chclient.DefaultApiService {
|
||||||
|
if clh.APIClient == nil {
|
||||||
|
clh.APIClient = clh.newAPIClient()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *CommandLineDirector) Build(builder CommandLineBuilder) (*CommandLine, error) {
|
return clh.APIClient
|
||||||
return builder.GetCommandLine()
|
}
|
||||||
|
|
||||||
|
func (clh *cloudHypervisor) newAPIClient() *chclient.DefaultApiService {
|
||||||
|
|
||||||
|
cfg := chclient.NewConfiguration()
|
||||||
|
|
||||||
|
socketTransport := &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, network, path string) (net.Conn, error) {
|
||||||
|
addr, err := net.ResolveUnixAddr("unix", clh.state.apiSocket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.DialUnix("unix", nil, addr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.HTTPClient = http.DefaultClient
|
||||||
|
cfg.HTTPClient.Transport = socketTransport
|
||||||
|
|
||||||
|
return chclient.NewAPIClient(cfg).DefaultApi
|
||||||
|
}
|
||||||
|
|
||||||
|
func openAPIClientError(err error) error {
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reason := ""
|
||||||
|
if apierr, ok := err.(chclient.GenericOpenAPIError); ok {
|
||||||
|
reason = string(apierr.Body())
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("error: %v reason: %s", err, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clh *cloudHypervisor) bootVM(ctx context.Context) error {
|
||||||
|
|
||||||
|
cl := clh.client()
|
||||||
|
|
||||||
|
if clh.config.Debug {
|
||||||
|
bodyBuf, err := json.Marshal(clh.vmconfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
clh.Logger().WithField("body", string(bodyBuf)).Debug("VM config")
|
||||||
|
}
|
||||||
|
_, err := cl.CreateVM(ctx, clh.vmconfig)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return openAPIClientError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, _, err := cl.VmInfoGet(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return openAPIClientError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clh.Logger().Debugf("VM state after create: %#v", info)
|
||||||
|
|
||||||
|
if info.State != "Created" {
|
||||||
|
return fmt.Errorf("VM state is not 'Created' after 'CreateVM'")
|
||||||
|
}
|
||||||
|
|
||||||
|
clh.Logger().Debug("Booting VM")
|
||||||
|
_, err = cl.BootVM(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return openAPIClientError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, _, err = cl.VmInfoGet(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return openAPIClientError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clh.Logger().Debugf("VM state after boot: %#v", info)
|
||||||
|
|
||||||
|
if info.State != "Running" {
|
||||||
|
return fmt.Errorf("VM state is not 'Running' after 'BootVM'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clh *cloudHypervisor) addVSock(cid int64, path string) {
|
||||||
|
clh.Logger().WithFields(log.Fields{
|
||||||
|
"path": path,
|
||||||
|
"cid": cid,
|
||||||
|
}).Info("Adding HybridVSock")
|
||||||
|
|
||||||
|
clh.vmconfig.Vsock = append(clh.vmconfig.Vsock, chclient.VsockConfig{Cid: cid, Sock: path})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (clh *cloudHypervisor) addNet(e Endpoint) {
|
||||||
|
clh.Logger().WithField("endpoint-type", e).Debugf("Adding Endpoint of type %v", e)
|
||||||
|
mac := e.HardwareAddr()
|
||||||
|
tapPath := e.NetworkPair().TapInterface.TAPIface.Name
|
||||||
|
clh.Logger().WithFields(log.Fields{
|
||||||
|
"mac": mac,
|
||||||
|
"tap": tapPath,
|
||||||
|
}).Info("Adding Net")
|
||||||
|
|
||||||
|
// FIXME: This is required by CH
|
||||||
|
// remove after PR is merged:
|
||||||
|
// https://github.com/cloud-hypervisor/cloud-hypervisor/pull/480
|
||||||
|
ip := "0.0.0.0"
|
||||||
|
mask := "0.0.0.0"
|
||||||
|
clh.vmconfig.Net = append(clh.vmconfig.Net, chclient.NetConfig{Mac: mac, Tap: tapPath, Ip: ip, Mask: mask})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add shared Volume using virtiofs
|
||||||
|
func (clh *cloudHypervisor) addVolume(volume types.Volume) error {
|
||||||
|
if clh.config.SharedFS != config.VirtioFS {
|
||||||
|
return fmt.Errorf("shared fs method not supported %s", clh.config.SharedFS)
|
||||||
|
}
|
||||||
|
|
||||||
|
vfsdSockPath, err := clh.virtioFsSocketPath(clh.id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if clh.config.VirtioFSCache == virtioFsCacheAlways {
|
||||||
|
clh.vmconfig.Fs = []chclient.FsConfig{
|
||||||
|
{
|
||||||
|
Tag: volume.MountTag,
|
||||||
|
CacheSize: int64(clh.config.VirtioFSCacheSize << 20),
|
||||||
|
Sock: vfsdSockPath,
|
||||||
|
NumQueues: clhFsQueues,
|
||||||
|
QueueSize: clhFsQueueSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clh.vmconfig.Fs = []chclient.FsConfig{
|
||||||
|
{
|
||||||
|
Tag: volume.MountTag,
|
||||||
|
Sock: vfsdSockPath,
|
||||||
|
NumQueues: clhFsQueues,
|
||||||
|
QueueSize: clhFsQueueSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
clh.Logger().Debug("Adding share volume to hypervisor: ", volume.MountTag)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user