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:
Jose Carlos Venegas Munoz 2019-11-25 07:48:47 +00:00
parent 60102188cd
commit f73723a23f
2 changed files with 313 additions and 283 deletions

View File

@ -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@"

View File

@ -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
} }