From 48d1eb943d1afce96aef612964a8056706bba597 Mon Sep 17 00:00:00 2001 From: Dmitry Lopatin Date: Tue, 14 Apr 2026 15:23:15 +0300 Subject: [PATCH 1/2] feat(vm): add disable-tap-veth-bridge setting Signed-off-by: Dmitry Lopatin --- .../cmd/virtualization-controller/main.go | 4 ++- .../pkg/common/annotations/annotations.go | 2 ++ .../pkg/config/load_network_settings.go | 31 +++++++++++++++++++ .../pkg/controller/kvbuilder/kvvm.go | 5 +++ .../pkg/controller/kvbuilder/kvvm_utils.go | 5 +++ .../controller/vm/internal/migrating_test.go | 2 +- .../vm/internal/service/migration_volumes.go | 22 +++++++------ .../pkg/controller/vm/internal/sync_kvvm.go | 30 ++++++++++-------- .../controller/vm/internal/sync_kvvm_test.go | 2 +- .../pkg/controller/vm/vm_controller.go | 5 +-- openapi/config-values.yaml | 13 ++++++++ .../virtualization-controller/_helpers.tpl | 2 ++ 12 files changed, 95 insertions(+), 28 deletions(-) create mode 100644 images/virtualization-artifact/pkg/config/load_network_settings.go diff --git a/images/virtualization-artifact/cmd/virtualization-controller/main.go b/images/virtualization-artifact/cmd/virtualization-controller/main.go index 45bbc5efcc..cb76caa4a5 100644 --- a/images/virtualization-artifact/cmd/virtualization-controller/main.go +++ b/images/virtualization-artifact/cmd/virtualization-controller/main.go @@ -207,6 +207,8 @@ func main() { os.Exit(1) } + networkSettings := appconfig.LoadNetworkSettingsFromEnv() + // Get a config to talk to the apiserver cfg, err := config.GetConfig() if err != nil { @@ -347,7 +349,7 @@ func main() { } vmLogger := logger.NewControllerLogger(vm.ControllerName, logLevel, logOutput, logDebugVerbosity, logDebugControllerList) - if err = vm.SetupController(ctx, mgr, virtClient, vmLogger, dvcrSettings, firmwareImage); err != nil { + if err = vm.SetupController(ctx, mgr, virtClient, vmLogger, dvcrSettings, firmwareImage, networkSettings.DisableTapVethBridge); err != nil { log.Error(err.Error()) os.Exit(1) } diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index 00e93d6805..44e1564d84 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -88,6 +88,8 @@ const ( // AnnVMRestartRequested is an annotation on KVVM that represents a request to restart a virtual machine. AnnVMRestartRequested = AnnAPIGroupV + "/vm-restart-requested" + // AnnDisableTapVethBridge disables tap-veth-bridge wiring for the VMI network setup in KubeVirt. + AnnDisableTapVethBridge = AnnAPIGroupV + "/disable-tap-veth-bridge" // AnnVMOPWorkloadUpdate is an annotation on vmop that represents a vmop created by workload-updater controller. AnnVMOPWorkloadUpdate = AnnAPIGroupV + "/workload-update" diff --git a/images/virtualization-artifact/pkg/config/load_network_settings.go b/images/virtualization-artifact/pkg/config/load_network_settings.go new file mode 100644 index 0000000000..762ad89e66 --- /dev/null +++ b/images/virtualization-artifact/pkg/config/load_network_settings.go @@ -0,0 +1,31 @@ +/* +Copyright 2026 Flant JSC + +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 config + +import "os" + +const DisableTapVethBridgeVar = "DISABLE_TAP_VETH_BRIDGE" + +type NetworkSettings struct { + DisableTapVethBridge bool +} + +func LoadNetworkSettingsFromEnv() NetworkSettings { + return NetworkSettings{ + DisableTapVethBridge: os.Getenv(DisableTapVethBridgeVar) == "true", + } +} diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 1a55e10b9b..9fadb91aa1 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -67,6 +67,7 @@ const ( type KVVMOptions struct { EnableParavirtualization bool OsType v1alpha2.OsType + DisableTapVethBridge bool // These options are for local development mode DisableHypervSyNIC bool @@ -92,6 +93,10 @@ func DefaultOptions(current *v1alpha2.VirtualMachine) KVVMOptions { } } +func (b *KVVM) Options() KVVMOptions { + return b.opts +} + func NewEmptyKVVM(name types.NamespacedName, opts KVVMOptions) *KVVM { return &KVVM{ opts: opts, diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go index 61edd64c33..2771238341 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go @@ -111,6 +111,11 @@ func ApplyVirtualMachineSpec( kvvm.SetUSBMigrationStrategy() kvvm.SetMetadata(vm.ObjectMeta) + if kvvm.Options().DisableTapVethBridge { + kvvm.SetKVVMIAnnotation(annotations.AnnDisableTapVethBridge, "true") + } else { + kvvm.RemoveKVVMIAnnotation(annotations.AnnDisableTapVethBridge) + } setNetwork(kvvm, networkSpec) kvvm.SetTablet("default-0") kvvm.SetNodeSelector(vm.Spec.NodeSelector, class.Spec.NodeSelector.MatchLabels) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/migrating_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/migrating_test.go index ecf10128e0..5c8dec6348 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/migrating_test.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/migrating_test.go @@ -92,7 +92,7 @@ var _ = Describe("MigratingHandler", func() { } reconcile := func() { - h := NewMigratingHandler(vmservice.NewMigrationVolumesService(fakeClient, MakeKVVMFromVMSpec, 10*time.Second)) + h := NewMigratingHandler(vmservice.NewMigrationVolumesService(fakeClient, MakeKVVMFromVMSpec, 10*time.Second, false)) _, err := h.Handle(ctx, vmState) Expect(err).NotTo(HaveOccurred()) err = resource.Update(context.Background()) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/service/migration_volumes.go b/images/virtualization-artifact/pkg/controller/vm/internal/service/migration_volumes.go index 5f208c9f1c..7e575be569 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/service/migration_volumes.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/service/migration_volumes.go @@ -47,18 +47,20 @@ import ( ) type MigrationVolumesService struct { - client client.Client - makeKVVMFromSpec func(ctx context.Context, s state.VirtualMachineState) (*virtv1.VirtualMachine, error) - delay map[types.UID]time.Time - delayDuration time.Duration + client client.Client + makeKVVMFromSpec func(ctx context.Context, s state.VirtualMachineState, disableTapVethBridge bool) (*virtv1.VirtualMachine, error) + delay map[types.UID]time.Time + delayDuration time.Duration + disableTapVethBridge bool } -func NewMigrationVolumesService(client client.Client, makeKVVMFromSpec func(ctx context.Context, s state.VirtualMachineState) (*virtv1.VirtualMachine, error), delayDuration time.Duration) *MigrationVolumesService { +func NewMigrationVolumesService(client client.Client, makeKVVMFromSpec func(ctx context.Context, s state.VirtualMachineState, disableTapVethBridge bool) (*virtv1.VirtualMachine, error), delayDuration time.Duration, disableTapVethBridge bool) *MigrationVolumesService { return &MigrationVolumesService{ - client: client, - makeKVVMFromSpec: makeKVVMFromSpec, - delay: make(map[types.UID]time.Time), - delayDuration: delayDuration, + client: client, + makeKVVMFromSpec: makeKVVMFromSpec, + delay: make(map[types.UID]time.Time), + delayDuration: delayDuration, + disableTapVethBridge: disableTapVethBridge, } } @@ -476,7 +478,7 @@ func (s MigrationVolumesService) fillContainerDiskImagePullPolicies(kvvm *virtv1 } func (s MigrationVolumesService) makeKVVMFromVirtualMachineSpec(ctx context.Context, vmState state.VirtualMachineState) (*virtv1.VirtualMachine, *virtv1.VirtualMachine, error) { - kvvm, err := s.makeKVVMFromSpec(ctx, vmState) + kvvm, err := s.makeKVVMFromSpec(ctx, vmState, s.disableTapVethBridge) if err != nil { return nil, nil, err } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index 56e7250a2d..6fa8658df8 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -62,22 +62,25 @@ func NewSyncKvvmHandler( recorder eventrecord.EventRecorderLogger, featureGate featuregate.FeatureGate, syncVolumesService syncVolumesService, + disableTapVethBridge bool, ) *SyncKvvmHandler { return &SyncKvvmHandler{ - dvcrSettings: dvcrSettings, - client: client, - recorder: recorder, - featureGate: featureGate, - syncVolumesService: syncVolumesService, + dvcrSettings: dvcrSettings, + client: client, + recorder: recorder, + featureGate: featureGate, + syncVolumesService: syncVolumesService, + disableTapVethBridge: disableTapVethBridge, } } type SyncKvvmHandler struct { - client client.Client - recorder eventrecord.EventRecorderLogger - dvcrSettings *dvcr.Settings - featureGate featuregate.FeatureGate - syncVolumesService syncVolumesService + client client.Client + recorder eventrecord.EventRecorderLogger + dvcrSettings *dvcr.Settings + featureGate featuregate.FeatureGate + syncVolumesService syncVolumesService + disableTapVethBridge bool } func (h *SyncKvvmHandler) Handle(ctx context.Context, s state.VirtualMachineState) (reconcile.Result, error) { @@ -343,7 +346,7 @@ func (h *SyncKvvmHandler) createKVVM(ctx context.Context, s state.VirtualMachine if s.VirtualMachine().IsEmpty() { return fmt.Errorf("the virtual machine is empty, please report a bug") } - kvvm, err := MakeKVVMFromVMSpec(ctx, s) + kvvm, err := MakeKVVMFromVMSpec(ctx, s, h.disableTapVethBridge) if err != nil { return fmt.Errorf("failed to make the internal virtual machine: %w", err) } @@ -372,7 +375,7 @@ func (h *SyncKvvmHandler) updateKVVM(ctx context.Context, s state.VirtualMachine return fmt.Errorf("the virtual machine is empty, please report a bug") } - newKVVM, err := MakeKVVMFromVMSpec(ctx, s) + newKVVM, err := MakeKVVMFromVMSpec(ctx, s, h.disableTapVethBridge) if err != nil { return fmt.Errorf("update internal virtual machine: make kvvm from the virtual machine spec: %w", err) } @@ -438,7 +441,7 @@ func saveKVVMDomainMemoryForPatching(prevKVVM, newKVVM *virtv1.VirtualMachine) * return nil } -func MakeKVVMFromVMSpec(ctx context.Context, s state.VirtualMachineState) (*virtv1.VirtualMachine, error) { +func MakeKVVMFromVMSpec(ctx context.Context, s state.VirtualMachineState, disableTapVethBridge bool) (*virtv1.VirtualMachine, error) { if s.VirtualMachine().IsEmpty() { return nil, nil } @@ -446,6 +449,7 @@ func MakeKVVMFromVMSpec(ctx context.Context, s state.VirtualMachineState) (*virt kvvmName := object.NamespacedName(current) kvvmOpts := kvbuilder.DefaultOptions(current) + kvvmOpts.DisableTapVethBridge = disableTapVethBridge kvvm, err := s.KVVM(ctx) if err != nil { diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm_test.go index 1e76e42ee3..5073ede77a 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm_test.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm_test.go @@ -138,7 +138,7 @@ var _ = Describe("SyncKvvmHandler", func() { } reconcile := func() { - h := NewSyncKvvmHandler(nil, fakeClient, recorder, featuregates.Default(), vmservice.NewMigrationVolumesService(fakeClient, MakeKVVMFromVMSpec, 10*time.Second)) + h := NewSyncKvvmHandler(nil, fakeClient, recorder, featuregates.Default(), vmservice.NewMigrationVolumesService(fakeClient, MakeKVVMFromVMSpec, 10*time.Second, false), false) _, err := h.Handle(ctx, vmState) Expect(err).NotTo(HaveOccurred()) err = resource.Update(context.Background()) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go index 21b3ba6100..44f9fd8b11 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go @@ -51,6 +51,7 @@ func SetupController( log *log.Logger, dvcrSettings *dvcr.Settings, firmwareImage string, + disableTapVethBridge bool, ) error { recorder := eventrecord.NewEventRecorderLogger(mgr, ControllerName) mgrCache := mgr.GetCache() @@ -58,7 +59,7 @@ func SetupController( blockDeviceService := service.NewBlockDeviceService(client) vmClassService := service.NewVirtualMachineClassService(client) - migrateVolumesService := vmservice.NewMigrationVolumesService(client, internal.MakeKVVMFromVMSpec, 10*time.Second) + migrateVolumesService := vmservice.NewMigrationVolumesService(client, internal.MakeKVVMFromVMSpec, 10*time.Second, disableTapVethBridge) handlers := []Handler{ internal.NewMaintenanceHandler(client), @@ -76,7 +77,7 @@ func SetupController( internal.NewPodHandler(client), internal.NewSizePolicyHandler(), internal.NewNetworkInterfaceHandler(featuregates.Default()), - internal.NewSyncKvvmHandler(dvcrSettings, client, recorder, featuregates.Default(), migrateVolumesService), + internal.NewSyncKvvmHandler(dvcrSettings, client, recorder, featuregates.Default(), migrateVolumesService, disableTapVethBridge), internal.NewSyncPowerStateHandler(client, recorder), internal.NewSyncMetadataHandler(client), internal.NewLifeCycleHandler(client, recorder), diff --git a/openapi/config-values.yaml b/openapi/config-values.yaml index e46d2362fc..3ded848587 100644 --- a/openapi/config-values.yaml +++ b/openapi/config-values.yaml @@ -252,6 +252,19 @@ properties: type: string minLength: 1 x-examples: ["sc-1", "sc-2"] + network: + type: object + description: | + Virtual machine networking settings. + properties: + disableTapVethBridge: + type: boolean + default: false + description: | + Disable tap-veth-bridge wiring for KubeVirt bridge interfaces. + + When enabled, the virtualization controller adds the `virtualization.deckhouse.io/disable-tap-veth-bridge=true` annotation to the KubeVirt VirtualMachineInstance template. + The KubeVirt network setup then skips creating the tap-veth-bridge wiring and bridge DHCP configurator for such VMIs. logLevel: type: string description: | diff --git a/templates/virtualization-controller/_helpers.tpl b/templates/virtualization-controller/_helpers.tpl index 3d799bf0fd..220c3a020c 100644 --- a/templates/virtualization-controller/_helpers.tpl +++ b/templates/virtualization-controller/_helpers.tpl @@ -18,6 +18,8 @@ true value: "1" - name: DISABLE_HYPERV_SYNIC value: "1" +- name: DISABLE_TAP_VETH_BRIDGE + value: {{ dig "network" "disableTapVethBridge" false .Values.virtualization.internal.moduleConfig | toString | quote }} - name: POD_NAMESPACE valueFrom: fieldRef: From f8264fcdf8e7ba10b8efb9d819e690d88f9b1736 Mon Sep 17 00:00:00 2001 From: Dmitry Lopatin Date: Tue, 14 Apr 2026 15:23:15 +0300 Subject: [PATCH 2/2] feat(vm): add disable-tap-veth-bridge setting Signed-off-by: Dmitry Lopatin --- build/components/versions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/components/versions.yml b/build/components/versions.yml index c2a2cb2bc9..d25bd9b984 100644 --- a/build/components/versions.yml +++ b/build/components/versions.yml @@ -3,7 +3,7 @@ firmware: libvirt: v10.9.0 edk2: stable202411 core: - 3p-kubevirt: v1.6.2-v12n.23 + 3p-kubevirt: feat/vm/disable-tap-veth-bridge 3p-containerized-data-importer: v1.60.3-v12n.18 distribution: 2.8.3 package: