@@ -31,6 +31,7 @@ import (
3131 "github.com/deckhouse/virtualization-controller/pkg/builder/vd"
3232 "github.com/deckhouse/virtualization-controller/pkg/builder/vm"
3333 "github.com/deckhouse/virtualization-controller/pkg/builder/vmop"
34+ "github.com/deckhouse/virtualization-controller/pkg/controller/conditions"
3435 "github.com/deckhouse/virtualization/api/core/v1alpha2"
3536 "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition"
3637 "github.com/deckhouse/virtualization/test/e2e/internal/framework"
@@ -45,6 +46,16 @@ type additionalNetworkTestCase struct {
4546 vmBarAdditionalIP string
4647}
4748
49+ const (
50+ additionalInterfaceVLANID = 4006
51+ secondAdditionalInterfaceVLANID = 4007
52+ )
53+
54+ func expectClusterNetworkExists (f * framework.Framework , vlanID int ) {
55+ Expect (util .IsClusterNetworkExists (f , vlanID )).To (BeTrue (),
56+ fmt .Sprintf ("Cluster network %s does not exist. Create it first: %s" , util .ClusterNetworkName (vlanID ), util .ClusterNetworkCreateCommand (vlanID )))
57+ }
58+
4859var _ = Describe ("VirtualMachineAdditionalNetworkInterfaces" , func () {
4960 var (
5061 vdFooRoot * v1alpha2.VirtualDisk
@@ -64,8 +75,8 @@ var _ = Describe("VirtualMachineAdditionalNetworkInterfaces", func() {
6475 Skip ("SDN module is disabled. Skipping test." )
6576 }
6677
67- Expect ( util . IsClusterNetworkExists ( f )). To ( BeTrue (),
68- fmt . Sprintf ( "Cluster network %s does not exist. Create it first: %s" , util . ClusterNetworkName , util . ClusterNetworkCreateCommand ) )
78+ expectClusterNetworkExists ( f , additionalInterfaceVLANID )
79+ expectClusterNetworkExists ( f , secondAdditionalInterfaceVLANID )
6980 })
7081
7182 DescribeTable ("verifies additional network interfaces and connectivity before and after migration" ,
@@ -146,6 +157,88 @@ var _ = Describe("VirtualMachineAdditionalNetworkInterfaces", func() {
146157 Entry ("Main + additional network" , additionalNetworkTestCase {vmBarHasMainNetwork : true , vmFooAdditionalIP : "192.168.42.10" , vmBarAdditionalIP : "192.168.42.11" }),
147158 Entry ("Only additional network (vm-bar without Main)" , additionalNetworkTestCase {vmBarHasMainNetwork : false , vmFooAdditionalIP : "192.168.42.12" , vmBarAdditionalIP : "192.168.42.13" }),
148159 )
160+
161+ Describe ("verifies interface name persistence after removing middle ClusterNetwork" , func () {
162+ var (
163+ vdRoot * v1alpha2.VirtualDisk
164+ vm * v1alpha2.VirtualMachine
165+ )
166+
167+ const (
168+ getLastInterfaceNameCmd = "ip -o link show | tail -1 | cut -d: -f2 | awk \" {print \\ $1}\" "
169+ )
170+
171+ It ("should preserve interface name after removing middle ClusterNetwork and rebooting" , func () {
172+ var lastInterfaceNameBeforeRemoval string
173+
174+ By ("Create VM with Main network and two additional ClusterNetworks" , func () {
175+ ns := f .Namespace ().Name
176+
177+ vdRoot = vd .New (
178+ vd .WithName ("vd-root" ),
179+ vd .WithNamespace (ns ),
180+ vd .WithDataSourceHTTP (& v1alpha2.DataSourceHTTP {
181+ URL : object .ImageURLUbuntu ,
182+ }),
183+ )
184+
185+ vm = buildVMWithNetworks ("vm" , ns , vdRoot .Name , "192.168.1.20" , true )
186+ vm .Spec .Networks = append (vm .Spec .Networks , v1alpha2.NetworksSpec {
187+ Type : v1alpha2 .NetworksTypeClusterNetwork ,
188+ Name : util .ClusterNetworkName (secondAdditionalInterfaceVLANID ),
189+ })
190+
191+ err := f .CreateWithDeferredDeletion (context .Background (), vdRoot , vm )
192+ Expect (err ).NotTo (HaveOccurred ())
193+
194+ util .UntilObjectPhase (string (v1alpha2 .MachineRunning ), framework .LongTimeout , vm )
195+ util .UntilVMAgentReady (crclient .ObjectKeyFromObject (vm ), framework .LongTimeout )
196+ util .UntilConditionStatus (vmcondition .TypeNetworkReady .String (), "True" , framework .LongTimeout , vm )
197+ })
198+
199+ By ("Get last interface name via SSH" , func () {
200+ util .UntilSSHReady (f , vm , framework .LongTimeout )
201+ output , err := f .SSHCommand (vm .Name , vm .Namespace , getLastInterfaceNameCmd )
202+ Expect (err ).NotTo (HaveOccurred ())
203+ lastInterfaceNameBeforeRemoval = strings .TrimSpace (output )
204+ Expect (lastInterfaceNameBeforeRemoval ).NotTo (BeEmpty (), "Failed to get last interface name" )
205+ })
206+
207+ By ("Remove middle ClusterNetwork from VM spec" , func () {
208+ err := f .Clients .GenericClient ().Get (context .Background (), crclient .ObjectKeyFromObject (vm ), vm )
209+ Expect (err ).NotTo (HaveOccurred ())
210+ vm .Spec .Networks = []v1alpha2.NetworksSpec {vm .Spec .Networks [0 ], vm .Spec .Networks [2 ]}
211+ err = f .Clients .GenericClient ().Update (context .Background (), vm )
212+ Expect (err ).NotTo (HaveOccurred ())
213+ })
214+
215+ By ("Reboot VM via VMOP" , func () {
216+ err := f .Clients .GenericClient ().Get (context .Background (), crclient .ObjectKeyFromObject (vm ), vm )
217+ Expect (err ).NotTo (HaveOccurred ())
218+
219+ runningCondition , _ := conditions .GetCondition (vmcondition .TypeRunning , vm .Status .Conditions )
220+ previousRunningTime := runningCondition .LastTransitionTime .Time
221+
222+ util .RebootVirtualMachineByVMOP (f , vm )
223+
224+ util .UntilVirtualMachineRebooted (crclient .ObjectKeyFromObject (vm ), previousRunningTime , framework .LongTimeout )
225+ util .UntilObjectPhase (string (v1alpha2 .MachineRunning ), framework .LongTimeout , vm )
226+ util .UntilVMAgentReady (crclient .ObjectKeyFromObject (vm ), framework .LongTimeout )
227+ util .UntilConditionStatus (vmcondition .TypeNetworkReady .String (), "True" , framework .LongTimeout , vm )
228+ })
229+
230+ By ("Verify last interface name has not changed" , func () {
231+ util .UntilSSHReady (f , vm , framework .LongTimeout )
232+ output , err := f .SSHCommand (vm .Name , vm .Namespace , getLastInterfaceNameCmd )
233+ Expect (err ).NotTo (HaveOccurred ())
234+ lastInterfaceNameAfterRemoval := strings .TrimSpace (output )
235+ Expect (lastInterfaceNameAfterRemoval ).NotTo (BeEmpty (), "Failed to get last interface name" )
236+
237+ Expect (lastInterfaceNameAfterRemoval ).To (Equal (lastInterfaceNameBeforeRemoval ),
238+ fmt .Sprintf ("Interface name changed from %s to %s after removing middle ClusterNetwork" , lastInterfaceNameBeforeRemoval , lastInterfaceNameAfterRemoval ))
239+ })
240+ })
241+ })
149242})
150243
151244// buildVMWithNetworks creates a VM with optional Main + ClusterNetwork.
@@ -175,7 +268,7 @@ func buildVMWithNetworks(name, ns, vdRootName, additionalIP string, hasMain bool
175268 opts = append (opts ,
176269 vm .WithNetwork (v1alpha2.NetworksSpec {
177270 Type : v1alpha2 .NetworksTypeClusterNetwork ,
178- Name : util .ClusterNetworkName ,
271+ Name : util .ClusterNetworkName ( additionalInterfaceVLANID ) ,
179272 }),
180273 )
181274 return vm .New (opts ... )
0 commit comments