Skip to content

Commit

Permalink
Merge pull request kubevirt#6201 from zcahana/allow_emulation
Browse files Browse the repository at this point in the history
Rename "UseEmulation" --> "AllowEmulation"
  • Loading branch information
kubevirt-bot authored Aug 24, 2021
2 parents 6fd2861 + bdbb79d commit 6873c76
Show file tree
Hide file tree
Showing 27 changed files with 148 additions and 118 deletions.
1 change: 1 addition & 0 deletions api/openapi-spec/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -10062,6 +10062,7 @@
"format": "int32"
},
"useEmulation": {
"description": "UseEmulation can be set to true to allow fallback to software emulation in case hardware-assisted emulation is not available.",
"type": "boolean"
}
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/virt-launcher/virt-launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ func main() {
uid := pflag.String("uid", "", "UID of the VirtualMachineInstance")
namespace := pflag.String("namespace", "", "Namespace of the VirtualMachineInstance")
gracePeriodSeconds := pflag.Int("grace-period-seconds", 30, "Grace period to observe before sending SIGTERM to vmi process")
useEmulation := pflag.Bool("use-emulation", false, "Use software emulation")
allowEmulation := pflag.Bool("allow-emulation", false, "Allow use of software emulation as fallback")
runWithNonRoot := pflag.Bool("run-as-nonroot", false, "Run libvirtd with the 'virt' user")
hookSidecars := pflag.Uint("hook-sidecars", 0, "Number of requested hook sidecars, virt-launcher will wait for all of them to become available")
noFork := pflag.Bool("no-fork", false, "Fork and let virt-launcher watch itself to react to crashes if set to false")
Expand Down Expand Up @@ -442,7 +442,7 @@ func main() {
// Start the virt-launcher command service.
// Clients can use this service to tell virt-launcher
// to start/stop virtual machines
options := cmdserver.NewServerOptions(*useEmulation)
options := cmdserver.NewServerOptions(*allowEmulation)
cmdclient.SetLegacyBaseDir(*virtShareDir)
cmdServerDone := startCmdServer(cmdclient.UninitializedSocketOnGuest(), domainManager, stopChan, options)

Expand Down
20 changes: 14 additions & 6 deletions docs/software-emulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ in case that at least one network interface model is virtio (note: if the NIC
model is not explicitly specified, by default virtio is chosen).

If `useEmulation` is enabled,
- hardware emulation via `/dev/kvm` will not be attempted. `qemu` will be used
for software emulation instead.
- in-kernel virtio-net backend emulation via `/dev/vhost-net` will not be
attempted. QEMU userland virtio NIC emulation will be used for virtio-net
interface instead.
- `qemu` will be used for software emulation, in case that hardware emulation
via `/dev/kvm` is unavailable.
- QEMU userland virtio NIC emulation will be used for virtio-net interfaces,
in case that in-kernel virtio-net backend emulation via `/dev/vhost-net`
is unavailable.

If `useEmulation` is disabled, and a required hardware emulation device is unavailable
(`/dev/kvm`, or `/dev/vhost-net` for a VirtualMachine which uses virtio for at least one interface),
the VirtualMachine will fail to start and an error will be reported.

Note that software emulation, when enabled, is only used as a fallback when
hardware emulation is not available. Hardware emulation is always attempted first,
regardless of the value of the `useEmulation`.

# Configuration

Enabling software emulation is a cluster-wide setting, and is activated by
editing the kubevirt-config as follows:
editing the `KubeVirt` CR as follows:

```bash
cluster-up/kubectl.sh --namespace kubevirt edit kubevirt kubevirt
Expand Down
6 changes: 6 additions & 0 deletions manifests/generated/kv-resource.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ spec:
pvcTolerateLessSpaceUpToPercent:
type: integer
useEmulation:
description: UseEmulation can be set to true to allow fallback
to software emulation in case hardware-assisted emulation
is not available.
type: boolean
type: object
emulatedMachines:
Expand Down Expand Up @@ -2256,6 +2259,9 @@ spec:
pvcTolerateLessSpaceUpToPercent:
type: integer
useEmulation:
description: UseEmulation can be set to true to allow fallback
to software emulation in case hardware-assisted emulation
is not available.
type: boolean
type: object
emulatedMachines:
Expand Down
16 changes: 12 additions & 4 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,27 @@ func IsVFIOVMI(vmi *v1.VirtualMachineInstance) bool {
return false
}

func NeedVirtioNetDevice(vmi *v1.VirtualMachineInstance, useEmulation bool) bool {
// WantVirtioNetDevice checks whether a VMI references at least one "virtio" network interface.
// Note that the reference can be explicit or implicit (unspecified nic models defaults to "virtio").
func WantVirtioNetDevice(vmi *v1.VirtualMachineInstance) bool {
for _, iface := range vmi.Spec.Domain.Devices.Interfaces {
if !useEmulation && (iface.Model == "" || iface.Model == "virtio") {
if iface.Model == "" || iface.Model == "virtio" {
return true
}
}
return false
}

// NeedVirtioNetDevice checks whether a VMI requires the presence of the "virtio" net device.
// This happens when the VMI wants to use a "virtio" network interface, and software emulation is disallowed.
func NeedVirtioNetDevice(vmi *v1.VirtualMachineInstance, allowEmulation bool) bool {
return WantVirtioNetDevice(vmi) && !allowEmulation
}

// UseSoftwareEmulationForDevice determines whether to fallback to software emulation for the given device.
// This happens when the given device doesn't exist, and software emulation is enabled.
func UseSoftwareEmulationForDevice(devicePath string, useEmulation bool) (bool, error) {
if !useEmulation {
func UseSoftwareEmulationForDevice(devicePath string, allowEmulation bool) (bool, error) {
if !allowEmulation {
return false, nil
}

Expand Down
10 changes: 5 additions & 5 deletions pkg/virt-config/config-map.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const (
FeatureGatesKey = "feature-gates"
EmulatedMachinesKey = "emulated-machines"
MachineTypeKey = "machine-type"
UseEmulationKey = "debug.useEmulation"
AllowEmulationKey = "debug.useEmulation"
ImagePullPolicyKey = "dev.imagePullPolicy"
MigrationsConfigKey = "migrations"
CPUModelKey = "default-cpu-model"
Expand Down Expand Up @@ -202,7 +202,7 @@ func defaultClusterConfig(cpuArch string) *v1.KubeVirtConfiguration {
return &v1.KubeVirtConfiguration{
ImagePullPolicy: DefaultImagePullPolicy,
DeveloperConfiguration: &v1.DeveloperConfiguration{
UseEmulation: DefaultUseEmulation,
UseEmulation: DefaultAllowEmulation,
MemoryOvercommit: DefaultMemoryOvercommit,
LessPVCSpaceToleration: DefaultLessPVCSpaceToleration,
MinimumReservePVCBytes: DefaultMinimumReservePVCBytes,
Expand Down Expand Up @@ -359,16 +359,16 @@ func setConfigFromConfigMap(config *v1.KubeVirtConfiguration, configMap *k8sv1.C
}

// set if emulation is used
useEmulation := strings.TrimSpace(configMap.Data[UseEmulationKey])
switch useEmulation {
allowEmulation := strings.TrimSpace(configMap.Data[AllowEmulationKey])
switch allowEmulation {
case "":
// keep the default
case "true":
config.DeveloperConfiguration.UseEmulation = true
case "false":
config.DeveloperConfiguration.UseEmulation = false
default:
return fmt.Errorf("invalid debug.useEmulation in config: %v", useEmulation)
return fmt.Errorf("invalid %s in config: %v", AllowEmulationKey, allowEmulation)
}

// set machine type
Expand Down
16 changes: 8 additions & 8 deletions pkg/virt-config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ var _ = Describe("ConfigMap", func() {
clusterConfig, _, _, _ := testutils.NewFakeClusterConfig(&kubev1.ConfigMap{
Data: map[string]string{"debug.useEmulation": value},
})
Expect(clusterConfig.IsUseEmulation()).To(Equal(result))
Expect(clusterConfig.AllowEmulation()).To(Equal(result))
},
table.Entry("is true, IsUseEmulation should return true", "true", true),
table.Entry("is false, IsUseEmulation should return false", "false", false),
table.Entry("when unset, IsUseEmulation should return false", "", false),
table.Entry("when invalid, IsUseEmulation should return the default", "invalid", false),
table.Entry("is true, AllowEmulation should return true", "true", true),
table.Entry("is false, AllowEmulation should return false", "false", false),
table.Entry("when unset, AllowEmulation should return false", "", false),
table.Entry("when invalid, AllowEmulation should return the default", "invalid", false),
)

table.DescribeTable(" when permitSlirpInterface", func(value string, result bool) {
Expand Down Expand Up @@ -485,7 +485,7 @@ var _ = Describe("ConfigMap", func() {
},
})

emulation := clusterConfig.IsUseEmulation()
emulation := clusterConfig.AllowEmulation()
Expect(emulation).To(BeTrue())

cminformer.GetStore().Add(&kubev1.ConfigMap{
Expand All @@ -494,10 +494,10 @@ var _ = Describe("ConfigMap", func() {
Name: virtconfig.ConfigMapName,
ResourceVersion: rand.String(10),
},
Data: map[string]string{virtconfig.UseEmulationKey: "false"},
Data: map[string]string{virtconfig.AllowEmulationKey: "false"},
})

emulation = clusterConfig.IsUseEmulation()
emulation = clusterConfig.AllowEmulation()
Expect(emulation).To(BeFalse())
})

Expand Down
4 changes: 2 additions & 2 deletions pkg/virt-config/virt-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const (
DefaultNodeSelectors = ""
DefaultNetworkInterface = "bridge"
DefaultImagePullPolicy = k8sv1.PullIfNotPresent
DefaultUseEmulation = false
DefaultAllowEmulation = false
DefaultUnsafeMigrationOverride = false
DefaultPermitSlirpInterface = false
SmbiosConfigDefaultFamily = "KubeVirt"
Expand Down Expand Up @@ -107,7 +107,7 @@ func (c *ClusterConfig) GetMemBalloonStatsPeriod() uint32 {
return *c.GetConfig().MemBalloonStatsPeriod
}

func (c *ClusterConfig) IsUseEmulation() bool {
func (c *ClusterConfig) AllowEmulation() bool {
return c.GetConfig().DeveloperConfiguration.UseEmulation
}

Expand Down
14 changes: 7 additions & 7 deletions pkg/virt-controller/services/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -1032,20 +1032,20 @@ func (t *templateService) renderLaunchManifest(vmi *v1.VirtualMachineInstance, t
}
}

useEmulation := t.clusterConfig.IsUseEmulation()
allowEmulation := t.clusterConfig.AllowEmulation()
imagePullPolicy := t.clusterConfig.GetImagePullPolicy()

if resources.Limits == nil {
resources.Limits = make(k8sv1.ResourceList)
}

extraResources := getRequiredResources(vmi, useEmulation)
extraResources := getRequiredResources(vmi, allowEmulation)
for key, val := range extraResources {
resources.Limits[key] = val
}

if useEmulation {
command = append(command, "--use-emulation")
if allowEmulation {
command = append(command, "--allow-emulation")
} else {
resources.Limits[KvmDevice] = resource.MustParse("1")
}
Expand Down Expand Up @@ -1769,15 +1769,15 @@ func getRequiredCapabilities(vmi *v1.VirtualMachineInstance, config *virtconfig.
return capabilities
}

func getRequiredResources(vmi *v1.VirtualMachineInstance, useEmulation bool) k8sv1.ResourceList {
func getRequiredResources(vmi *v1.VirtualMachineInstance, allowEmulation bool) k8sv1.ResourceList {
res := k8sv1.ResourceList{}
if (len(vmi.Spec.Domain.Devices.Interfaces) > 0) ||
(vmi.Spec.Domain.Devices.AutoattachPodInterface == nil) ||
(*vmi.Spec.Domain.Devices.AutoattachPodInterface == true) {
res[TunDevice] = resource.MustParse("1")
}
if util.NeedVirtioNetDevice(vmi, useEmulation) {
// Note that about network interface, useEmulation does not make
if util.NeedVirtioNetDevice(vmi, allowEmulation) {
// Note that about network interface, allowEmulation does not make
// any difference on eventual Domain xml, but uniformly making
// /dev/vhost-net unavailable and libvirt implicitly fallback
// to use QEMU userland NIC emulation.
Expand Down
17 changes: 8 additions & 9 deletions pkg/virt-handler/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,11 +509,10 @@ func (d *VirtualMachineController) setPodNetworkPhase1(vmi *v1.VirtualMachineIns
return false, nil
}

if virtutil.IsNonRootVMI(vmi) && virtutil.NeedVirtioNetDevice(vmi, d.clusterConfig.IsUseEmulation()) {
vhostNet := path.Join(res.MountRoot(), "dev", "vhost-net")
err := diskutils.DefaultOwnershipManager.SetFileOwnership(vhostNet)
if virtutil.IsNonRootVMI(vmi) && virtutil.WantVirtioNetDevice(vmi) {
err := d.claimDeviceOwnership(vmi, "vhost-net")
if err != nil {
return true, fmt.Errorf("Failed to set up vhost-net device, %s", err)
return true, fmt.Errorf("failed to set up vhost-net device, %s", err)
}
}

Expand Down Expand Up @@ -2426,7 +2425,7 @@ func (d *VirtualMachineController) vmUpdateHelperMigrationTarget(origVMI *v1.Vir

}

err = d.claimKVMDeviceOwnership(vmi)
err = d.claimDeviceOwnership(vmi, "kvm")
if err != nil {
return fmt.Errorf("failed to set up file ownership for /dev/kvm: %v", err)
}
Expand Down Expand Up @@ -2519,7 +2518,7 @@ func (d *VirtualMachineController) vmUpdateHelperDefault(origVMI *v1.VirtualMach

}

err = d.claimKVMDeviceOwnership(vmi)
err = d.claimDeviceOwnership(vmi, "kvm")
if err != nil {
return fmt.Errorf("failed to set up file ownership for /dev/kvm: %v", err)
}
Expand Down Expand Up @@ -2799,15 +2798,15 @@ func (d *VirtualMachineController) isHostModelMigratable(vmi *v1.VirtualMachineI
return nil
}

func (d *VirtualMachineController) claimKVMDeviceOwnership(vmi *v1.VirtualMachineInstance) error {
func (d *VirtualMachineController) claimDeviceOwnership(vmi *v1.VirtualMachineInstance, deviceName string) error {
isolation, err := d.podIsolationDetector.Detect(vmi)
if err != nil {
return err
}

kvmPath := path.Join(isolation.MountRoot(), "dev", "kvm")
kvmPath := path.Join(isolation.MountRoot(), "dev", deviceName)

softwareEmulation, err := util.UseSoftwareEmulationForDevice(kvmPath, d.clusterConfig.IsUseEmulation())
softwareEmulation, err := util.UseSoftwareEmulationForDevice(kvmPath, d.clusterConfig.AllowEmulation())
if err != nil || softwareEmulation {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ var _ = Describe("AccessCredentials", func() {
c := &converter.ConverterContext{
Architecture: runtime.GOARCH,
VirtualMachine: vmi,
UseEmulation: true,
AllowEmulation: true,
SMBios: &cmdv1.SMBios{},
}
Expect(converter.Convert_v1_VirtualMachineInstance_To_api_Domain(vmi, domain, c)).To(Succeed())
Expand Down
22 changes: 11 additions & 11 deletions pkg/virt-launcher/virtwrap/cmd-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ const (
)

type ServerOptions struct {
useEmulation bool
allowEmulation bool
}

func NewServerOptions(useEmulation bool) *ServerOptions {
return &ServerOptions{useEmulation: useEmulation}
func NewServerOptions(allowEmulation bool) *ServerOptions {
return &ServerOptions{allowEmulation: allowEmulation}
}

type Launcher struct {
domainManager virtwrap.DomainManager
useEmulation bool
domainManager virtwrap.DomainManager
allowEmulation bool
}

func getVMIFromRequest(request *cmdv1.VMI) (*v1.VirtualMachineInstance, *cmdv1.Response) {
Expand Down Expand Up @@ -161,7 +161,7 @@ func (l *Launcher) SyncMigrationTarget(_ context.Context, request *cmdv1.VMIRequ
return response, nil
}

if err := l.domainManager.PrepareMigrationTarget(vmi, l.useEmulation); err != nil {
if err := l.domainManager.PrepareMigrationTarget(vmi, l.allowEmulation); err != nil {
log.Log.Object(vmi).Reason(err).Errorf("Failed to prepare migration target pod")
response.Success = false
response.Message = getErrorMessage(err)
Expand All @@ -180,7 +180,7 @@ func (l *Launcher) SyncVirtualMachine(_ context.Context, request *cmdv1.VMIReque
return response, nil
}

if _, err := l.domainManager.SyncVMI(vmi, l.useEmulation, request.Options); err != nil {
if _, err := l.domainManager.SyncVMI(vmi, l.allowEmulation, request.Options); err != nil {
log.Log.Object(vmi).Reason(err).Errorf("Failed to sync vmi")
response.Success = false
response.Message = getErrorMessage(err)
Expand Down Expand Up @@ -518,15 +518,15 @@ func RunServer(socketPath string,
stopChan chan struct{},
options *ServerOptions) (chan struct{}, error) {

useEmulation := false
allowEmulation := false
if options != nil {
useEmulation = options.useEmulation
allowEmulation = options.allowEmulation
}

grpcServer := grpc.NewServer([]grpc.ServerOption{}...)
server := &Launcher{
domainManager: domainManager,
useEmulation: useEmulation,
domainManager: domainManager,
allowEmulation: allowEmulation,
}
registerInfoServer(grpcServer)

Expand Down
8 changes: 4 additions & 4 deletions pkg/virt-launcher/virtwrap/cmd-server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ var _ = Describe("Virt remote commands", func() {
var shareDir string
var stop chan struct{}
var stopped bool
var useEmulation bool
var allowEmulation bool
var options *ServerOptions

BeforeEach(func() {
Expand All @@ -65,8 +65,8 @@ var _ = Describe("Virt remote commands", func() {

socketPath := filepath.Join(shareDir, "server.sock")

useEmulation = true
options = NewServerOptions(useEmulation)
allowEmulation = true
options = NewServerOptions(allowEmulation)
RunServer(socketPath, domainManager, stop, options)
client, err = cmdclient.NewClient(socketPath)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -85,7 +85,7 @@ var _ = Describe("Virt remote commands", func() {
It("should start a vmi", func() {
vmi := v1.NewVMIReferenceFromName("testvmi")
domain := api.NewMinimalDomain("testvmi")
domainManager.EXPECT().SyncVMI(vmi, useEmulation, &cmdv1.VirtualMachineOptions{}).Return(&domain.Spec, nil)
domainManager.EXPECT().SyncVMI(vmi, allowEmulation, &cmdv1.VirtualMachineOptions{}).Return(&domain.Spec, nil)

err := client.SyncVirtualMachine(vmi, &cmdv1.VirtualMachineOptions{})
Expect(err).ToNot(HaveOccurred())
Expand Down
Loading

0 comments on commit 6873c76

Please sign in to comment.