diff --git a/pkg/virt-handler/vm.go b/pkg/virt-handler/vm.go index 0c8d6079ef14..648761e90849 100644 --- a/pkg/virt-handler/vm.go +++ b/pkg/virt-handler/vm.go @@ -1255,37 +1255,42 @@ func calculatePausedCondition(vmi *v1.VirtualMachineInstance, reason api.StateCh } } -func (d *VirtualMachineController) calculateLiveMigrationCondition(vmi *v1.VirtualMachineInstance, hasHotplug bool) (*v1.VirtualMachineInstanceCondition, bool) { - liveMigrationCondition := v1.VirtualMachineInstanceCondition{ - Type: v1.VirtualMachineInstanceIsMigratable, - Status: k8sv1.ConditionTrue, - } - - setNonMigratable := func(err error, reason string) { - liveMigrationCondition.Status = k8sv1.ConditionFalse - liveMigrationCondition.Message = err.Error() - liveMigrationCondition.Reason = reason +func newNonMigratableCondition(msg string, reason string) *v1.VirtualMachineInstanceCondition { + return &v1.VirtualMachineInstanceCondition{ + Type: v1.VirtualMachineInstanceIsMigratable, + Status: k8sv1.ConditionFalse, + Message: msg, + Reason: reason, } +} +func (d *VirtualMachineController) calculateLiveMigrationCondition(vmi *v1.VirtualMachineInstance, hasHotplug bool) (*v1.VirtualMachineInstanceCondition, bool) { isBlockMigration, err := d.checkVolumesForMigration(vmi) if err != nil { - setNonMigratable(err, v1.VirtualMachineInstanceReasonDisksNotMigratable) - return &liveMigrationCondition, isBlockMigration + return newNonMigratableCondition(err.Error(), v1.VirtualMachineInstanceReasonDisksNotMigratable), isBlockMigration } + err = d.checkNetworkInterfacesForMigration(vmi) if err != nil { - setNonMigratable(err, v1.VirtualMachineInstanceReasonInterfaceNotMigratable) - return &liveMigrationCondition, isBlockMigration + return newNonMigratableCondition(err.Error(), v1.VirtualMachineInstanceReasonInterfaceNotMigratable), isBlockMigration } + if hasHotplug { - setNonMigratable(fmt.Errorf("VMI has hotplugged disks"), v1.VirtualMachineInstanceReasonHotplugNotMigratable) - return &liveMigrationCondition, isBlockMigration + return newNonMigratableCondition("VMI has hotplugged disks", v1.VirtualMachineInstanceReasonHotplugNotMigratable), isBlockMigration } + if err := d.isHostModelMigratable(vmi); err != nil { - setNonMigratable(err, v1.VirtualMachineInstanceReasonCPUModeNotMigratable) - return &liveMigrationCondition, isBlockMigration + return newNonMigratableCondition(err.Error(), v1.VirtualMachineInstanceReasonCPUModeNotMigratable), isBlockMigration + } + + if util.IsVMIVirtiofsEnabled(vmi) { + return newNonMigratableCondition("VMI uses virtiofs", v1.VirtualMachineInstanceReasonVirtIOFSNotMigratable), isBlockMigration } - return &liveMigrationCondition, isBlockMigration + + return &v1.VirtualMachineInstanceCondition{ + Type: v1.VirtualMachineInstanceIsMigratable, + Status: k8sv1.ConditionTrue, + }, isBlockMigration } func (c *VirtualMachineController) Run(threadiness int, stopCh chan struct{}) { diff --git a/pkg/virt-handler/vm_test.go b/pkg/virt-handler/vm_test.go index f17727c67b67..0d3a74aa0833 100644 --- a/pkg/virt-handler/vm_test.go +++ b/pkg/virt-handler/vm_test.go @@ -2158,6 +2158,22 @@ var _ = Describe("VirtualMachineInstance", func() { table.Entry("don't exist migration should fail", false), ) + It("should not be allowed to live-migrate if the VMI uses virtiofs ", func() { + vmi := v1.NewMinimalVMI("testvmi") + vmi.Spec.Domain.Devices.Filesystems = []v1.Filesystem{ + { + Name: "VIRTIOFS", + Virtiofs: &v1.FilesystemVirtiofs{}, + }, + } + + condition, isBlockMigration := controller.calculateLiveMigrationCondition(vmi, false) + Expect(isBlockMigration).To(BeFalse()) + Expect(condition.Type).To(Equal(v1.VirtualMachineInstanceIsMigratable)) + Expect(condition.Status).To(Equal(k8sv1.ConditionFalse)) + Expect(condition.Reason).To(Equal(v1.VirtualMachineInstanceReasonVirtIOFSNotMigratable)) + }) + Context("with network configuration", func() { It("should block migration for bridge binding assigned to the pod network", func() { vmi := v1.NewMinimalVMI("testvmi") diff --git a/staging/src/kubevirt.io/client-go/api/v1/types.go b/staging/src/kubevirt.io/client-go/api/v1/types.go index f363fd7ded59..d3d079e9d3cb 100644 --- a/staging/src/kubevirt.io/client-go/api/v1/types.go +++ b/staging/src/kubevirt.io/client-go/api/v1/types.go @@ -364,10 +364,12 @@ const ( VirtualMachineInstanceReasonDisksNotMigratable = "DisksNotLiveMigratable" // Reason means that VMI is not live migratioable because of it's network interfaces collection VirtualMachineInstanceReasonInterfaceNotMigratable = "InterfaceNotLiveMigratable" - // Reason means that VMI is not live migratioable because of it's network interfaces collection + // Reason means that VMI is not live migratioable because it uses hotplug VirtualMachineInstanceReasonHotplugNotMigratable = "HotplugNotLiveMigratable" // Reason means that VMI is not live migratioable because of it's CPU mode VirtualMachineInstanceReasonCPUModeNotMigratable = "CPUModeLiveMigratable" + // Reason means that VMI is not live migratable because it uses virtiofs + VirtualMachineInstanceReasonVirtIOFSNotMigratable = "VirtIOFSNotLiveMigratable" ) const ( diff --git a/tests/migration_test.go b/tests/migration_test.go index cf38234b55a8..5f2d754d474b 100644 --- a/tests/migration_test.go +++ b/tests/migration_test.go @@ -387,12 +387,16 @@ var _ = Describe("[Serial][rfe_id:393][crit:high][vendor:cnv-qe@redhat.com][leve By("Checking that the VirtualMachineInstance console has expected output") Expect(console.LoginToAlpine(vmi)).To(Succeed()) + gotExpectedCondition := false for _, c := range vmi.Status.Conditions { if c.Type == v1.VirtualMachineInstanceIsMigratable { Expect(c.Status).To(Equal(k8sv1.ConditionFalse)) + gotExpectedCondition = true } } + Expect(gotExpectedCondition).Should(BeTrue()) + // execute a migration, wait for finalized state migration := tests.NewRandomMigration(vmi.Name, vmi.Namespace) @@ -889,11 +893,14 @@ var _ = Describe("[Serial][rfe_id:393][crit:high][vendor:cnv-qe@redhat.com][leve By("Checking that the VirtualMachineInstance console has expected output") Expect(console.LoginToAlpine(vmi)).To(Succeed()) + gotExpectedCondition := false for _, c := range vmi.Status.Conditions { if c.Type == v1.VirtualMachineInstanceIsMigratable { Expect(c.Status).To(Equal(k8sv1.ConditionFalse)) + gotExpectedCondition = true } } + Expect(gotExpectedCondition).Should(BeTrue()) // execute a migration, wait for finalized state migration := tests.NewRandomMigration(vmi.Name, vmi.Namespace) @@ -1698,17 +1705,20 @@ var _ = Describe("[Serial][rfe_id:393][crit:high][vendor:cnv-qe@redhat.com][leve // Start the VirtualMachineInstance with the PVC attached vmi := tests.NewRandomVMIWithPVC(pvName) tests.AddUserData(vmi, "cloud-init", "#!/bin/bash\necho 'hello'\n") - vmi.Spec.Hostname = fmt.Sprintf("%s", cd.ContainerDiskCirros) + vmi.Spec.Hostname = string(cd.ContainerDiskCirros) vmi = runVMIAndExpectLaunch(vmi, 180) By("Checking that the VirtualMachineInstance console has expected output") Expect(libnet.WithIPv6(console.LoginToCirros)(vmi)).To(Succeed()) + gotExpectedCondition := false for _, c := range vmi.Status.Conditions { if c.Type == v1.VirtualMachineInstanceIsMigratable { Expect(c.Status).To(Equal(k8sv1.ConditionFalse)) + gotExpectedCondition = true } } + Expect(gotExpectedCondition).Should(BeTrue()) // execute a migration, wait for finalized state migration := tests.NewRandomMigration(vmi.Name, vmi.Namespace)