go.ligato.io/vpp-agent/v3@v3.5.0/tests/e2e/020_netalloc_test.go (about)

     1  //  Copyright (c) 2019 Cisco and/or its affiliates.
     2  //
     3  //  Licensed under the Apache License, Version 2.0 (the "License");
     4  //  you may not use this file except in compliance with the License.
     5  //  You may obtain a copy of the License at:
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //  Unless required by applicable law or agreed to in writing, software
    10  //  distributed under the License is distributed on an "AS IS" BASIS,
    11  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //  See the License for the specific language governing permissions and
    13  //  limitations under the License.
    14  
    15  package e2e
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	. "github.com/onsi/gomega"
    22  
    23  	"go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler"
    24  	linux_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    25  	linux_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/linux/l3"
    26  	linux_namespace "go.ligato.io/vpp-agent/v3/proto/ligato/linux/namespace"
    27  	"go.ligato.io/vpp-agent/v3/proto/ligato/netalloc"
    28  	vpp_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    29  	vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    30  	. "go.ligato.io/vpp-agent/v3/tests/e2e/e2etest"
    31  )
    32  
    33  // test IP address allocation using the netalloc plugin for VPP+Linux interfaces,
    34  // Linux routes and Linux ARPs in a topology where the interface is a neighbour
    35  // of the associated GW.
    36  //
    37  // topology + addressing:
    38  //
    39  //	VPP loop (192.168.10.1/24) <--> VPP tap (192.168.11.1/24) <--> Linux tap (192.168.11.2/24)
    40  //
    41  // topology + addressing AFTER CHANGE:
    42  //
    43  //	VPP loop (192.168.20.1/24) <--> VPP tap (192.168.12.1/24) <--> Linux tap (192.168.12.2/24)
    44  func TestIPWithNeighGW(t *testing.T) {
    45  	ctx := Setup(t)
    46  	defer ctx.Teardown()
    47  
    48  	const (
    49  		networkName      = "net1"
    50  		vppLoopName      = "vpp-loop"
    51  		vppTapName       = "vpp-tap"
    52  		linuxTapName     = "linux-tap"
    53  		linuxTapHostname = "tap"
    54  		vppLoopIP        = "192.168.10.1"
    55  		vppLoopIP2       = "192.168.20.1"
    56  		vppTapIP         = "192.168.11.1"
    57  		vppTapIP2        = "192.168.12.1"
    58  		vppTapHw         = "aa:aa:aa:bb:bb:bb"
    59  		linuxTapIP       = "192.168.11.2"
    60  		linuxTapIP2      = "192.168.12.2"
    61  		linuxTapHw       = "cc:cc:cc:dd:dd:dd"
    62  		netMask          = "/24"
    63  		msName           = "microservice1"
    64  	)
    65  
    66  	// ------- addresses:
    67  
    68  	vppLoopAddr := &netalloc.IPAllocation{
    69  		NetworkName:   networkName,
    70  		InterfaceName: vppLoopName,
    71  		Address:       vppLoopIP + netMask,
    72  	}
    73  
    74  	vppTapAddr := &netalloc.IPAllocation{
    75  		NetworkName:   networkName,
    76  		InterfaceName: vppTapName,
    77  		Address:       vppTapIP + netMask,
    78  		Gw:            linuxTapIP,
    79  	}
    80  
    81  	linuxTapAddr := &netalloc.IPAllocation{
    82  		NetworkName:   networkName,
    83  		InterfaceName: linuxTapName,
    84  		Address:       linuxTapIP + netMask,
    85  		Gw:            vppTapIP,
    86  	}
    87  
    88  	// ------- network items:
    89  
    90  	vppLoop := &vpp_interfaces.Interface{
    91  		Name:        vppLoopName,
    92  		Type:        vpp_interfaces.Interface_SOFTWARE_LOOPBACK,
    93  		Enabled:     true,
    94  		IpAddresses: []string{"alloc:" + networkName},
    95  	}
    96  
    97  	vppTap := &vpp_interfaces.Interface{
    98  		Name:        vppTapName,
    99  		Type:        vpp_interfaces.Interface_TAP,
   100  		Enabled:     true,
   101  		IpAddresses: []string{"alloc:" + networkName},
   102  		PhysAddress: vppTapHw,
   103  		Link: &vpp_interfaces.Interface_Tap{
   104  			Tap: &vpp_interfaces.TapLink{
   105  				Version:        2,
   106  				ToMicroservice: MsNamePrefix + msName,
   107  			},
   108  		},
   109  	}
   110  
   111  	linuxTap := &linux_interfaces.Interface{
   112  		Name:        linuxTapName,
   113  		Type:        linux_interfaces.Interface_TAP_TO_VPP,
   114  		Enabled:     true,
   115  		IpAddresses: []string{"alloc:" + networkName},
   116  		HostIfName:  linuxTapHostname,
   117  		PhysAddress: linuxTapHw,
   118  		Link: &linux_interfaces.Interface_Tap{
   119  			Tap: &linux_interfaces.TapLink{
   120  				VppTapIfName: vppTapName,
   121  			},
   122  		},
   123  		Namespace: &linux_namespace.NetNamespace{
   124  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   125  			Reference: MsNamePrefix + msName,
   126  		},
   127  	}
   128  
   129  	linuxArp := &linux_l3.ARPEntry{
   130  		Interface: linuxTapName,
   131  		IpAddress: "alloc:" + networkName + "/" + vppTapName,
   132  		HwAddress: vppTapHw,
   133  	}
   134  
   135  	linuxRoute := &linux_l3.Route{
   136  		OutgoingInterface: linuxTapName,
   137  		Scope:             linux_l3.Route_GLOBAL,
   138  		DstNetwork:        "alloc:" + networkName + "/" + vppLoopName,
   139  		GwAddr:            "alloc:" + networkName + "/GW",
   140  	}
   141  
   142  	ctx.StartMicroservice(msName)
   143  	req := ctx.GenericClient().ChangeRequest()
   144  	err := req.Update(
   145  		vppLoopAddr, vppTapAddr, linuxTapAddr,
   146  		vppLoop, vppTap, linuxTap,
   147  		linuxArp, linuxRoute,
   148  	).Send(context.Background())
   149  	ctx.Expect(err).ToNot(HaveOccurred())
   150  
   151  	checkItemsAreConfigured := func(msRestart, withLoopAddr bool) {
   152  		// configured immediately:
   153  		if withLoopAddr {
   154  			ctx.Expect(ctx.GetValueState(vppLoopAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   155  		}
   156  		ctx.Expect(ctx.GetValueState(vppTapAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   157  		ctx.Expect(ctx.GetValueState(linuxTapAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   158  		ctx.Expect(ctx.GetValueState(vppLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   159  		// the rest depends on the microservice
   160  		if msRestart {
   161  			ctx.Eventually(ctx.GetValueStateClb(vppTap)).Should(Equal(kvscheduler.ValueState_CONFIGURED))
   162  		} else {
   163  			ctx.Expect(ctx.GetValueState(vppTap)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   164  		}
   165  		ctx.Expect(ctx.GetValueState(linuxTap)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   166  		ctx.Expect(ctx.GetValueState(linuxArp)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   167  		if withLoopAddr {
   168  			ctx.Expect(ctx.GetValueState(linuxRoute)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   169  		} else {
   170  			ctx.Expect(ctx.GetValueState(linuxRoute)).To(Equal(kvscheduler.ValueState_PENDING))
   171  		}
   172  	}
   173  	checkItemsAreConfigured(true, true)
   174  
   175  	// check connection with ping
   176  	ctx.Expect(ctx.PingFromVPP(linuxTapIP)).To(Succeed())
   177  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP)).To(Succeed())
   178  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   179  
   180  	// restart microservice
   181  	ctx.StopMicroservice(msName)
   182  	ctx.StartMicroservice(msName)
   183  	checkItemsAreConfigured(true, true)
   184  
   185  	// check connection with ping (few packets will get lost before tables are refreshed)
   186  	ctx.PingFromVPP(linuxTapIP)
   187  	ctx.Expect(ctx.PingFromVPP(linuxTapIP)).To(Succeed())
   188  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP)).To(Succeed())
   189  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   190  
   191  	// change IP addresses - the network items should be re-created
   192  	vppLoopAddr.Address = vppLoopIP2 + netMask
   193  	vppTapAddr.Address = vppTapIP2 + netMask
   194  	vppTapAddr.Gw = linuxTapIP2
   195  	linuxTapAddr.Address = linuxTapIP2 + netMask
   196  	linuxTapAddr.Gw = vppTapIP2
   197  
   198  	req = ctx.GenericClient().ChangeRequest()
   199  	err = req.Update(vppLoopAddr, vppTapAddr, linuxTapAddr).Send(context.Background())
   200  	ctx.Expect(err).ToNot(HaveOccurred())
   201  	checkItemsAreConfigured(false, true)
   202  
   203  	// check connection with ping
   204  	ctx.Expect(ctx.PingFromVPP(linuxTapIP)).NotTo(Succeed())
   205  
   206  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP)).NotTo(Succeed())
   207  	ctx.Expect(ctx.PingFromVPP(linuxTapIP2)).To(Succeed())
   208  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP2)).To(Succeed())
   209  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   210  
   211  	// de-allocate loopback IP - the connection should not work anymore
   212  	req = ctx.GenericClient().ChangeRequest()
   213  	err = req.Delete(vppLoopAddr).Send(context.Background())
   214  	ctx.Expect(err).ToNot(HaveOccurred())
   215  
   216  	// loopback is still created but without IP and route is pending
   217  	checkItemsAreConfigured(false, false)
   218  
   219  	// can ping linux TAP from VPP, but cannot ping loopback
   220  	ctx.Expect(ctx.PingFromVPP(linuxTapIP2)).To(Succeed())
   221  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP2)).NotTo(Succeed())
   222  
   223  	// TODO: not in-sync - the list of IP addresses is updated in the metadata
   224  	//  - we need to figure out how to get rid of this and how to solve VRF-related
   225  	//    dependencies with netalloc'd IP addresses
   226  	//Expect(ctx.agentInSync()).To(BeTrue())
   227  }
   228  
   229  // test IP address allocation using the netalloc plugin for VPP+Linux interfaces,
   230  // Linux routes and Linux ARPs in a topology where the interface is NOT a neighbour
   231  // of the associated GW.
   232  //
   233  // topology + addressing (note the single-host network mask for Linux TAP):
   234  //
   235  //	VPP loop (192.168.10.1/24) <--> VPP tap (192.168.11.1/24) <--> Linux tap (192.168.11.2/32)
   236  //
   237  // topology + addressing AFTER CHANGE:
   238  //
   239  //	VPP loop (192.168.20.1/24) <--> VPP tap (192.168.12.1/24) <--> Linux tap (192.168.12.2/32)
   240  func TestIPWithNonLocalGW(t *testing.T) {
   241  	ctx := Setup(t)
   242  	defer ctx.Teardown()
   243  
   244  	const (
   245  		networkName      = "net1"
   246  		vppLoopName      = "vpp-loop"
   247  		vppTapName       = "vpp-tap"
   248  		linuxTapName     = "linux-tap"
   249  		linuxTapHostname = "tap"
   250  		vppLoopIP        = "192.168.10.1"
   251  		vppLoopIP2       = "192.168.20.1"
   252  		vppTapIP         = "192.168.11.1"
   253  		vppTapIP2        = "192.168.12.1"
   254  		vppTapHw         = "aa:aa:aa:bb:bb:bb"
   255  		linuxTapIP       = "192.168.11.2"
   256  		linuxTapIP2      = "192.168.12.2"
   257  		linuxTapHw       = "cc:cc:cc:dd:dd:dd"
   258  		vppNetMask       = "/24"
   259  		linuxNetMask     = "/32"
   260  		msName           = "microservice1"
   261  	)
   262  
   263  	// ------- addresses:
   264  
   265  	vppLoopAddr := &netalloc.IPAllocation{
   266  		NetworkName:   networkName,
   267  		InterfaceName: vppLoopName,
   268  		Address:       vppLoopIP + vppNetMask,
   269  	}
   270  
   271  	vppTapAddr := &netalloc.IPAllocation{
   272  		NetworkName:   networkName,
   273  		InterfaceName: vppTapName,
   274  		Address:       vppTapIP + vppNetMask,
   275  		Gw:            linuxTapIP,
   276  	}
   277  
   278  	linuxTapAddr := &netalloc.IPAllocation{
   279  		NetworkName:   networkName,
   280  		InterfaceName: linuxTapName,
   281  		Address:       linuxTapIP + linuxNetMask,
   282  		Gw:            vppTapIP,
   283  	}
   284  
   285  	// ------- network items:
   286  
   287  	vppLoop := &vpp_interfaces.Interface{
   288  		Name:        vppLoopName,
   289  		Type:        vpp_interfaces.Interface_SOFTWARE_LOOPBACK,
   290  		Enabled:     true,
   291  		IpAddresses: []string{"alloc:" + networkName},
   292  	}
   293  
   294  	vppTap := &vpp_interfaces.Interface{
   295  		Name:        vppTapName,
   296  		Type:        vpp_interfaces.Interface_TAP,
   297  		Enabled:     true,
   298  		IpAddresses: []string{"alloc:" + networkName},
   299  		PhysAddress: vppTapHw,
   300  		Link: &vpp_interfaces.Interface_Tap{
   301  			Tap: &vpp_interfaces.TapLink{
   302  				Version:        2,
   303  				ToMicroservice: MsNamePrefix + msName,
   304  			},
   305  		},
   306  	}
   307  
   308  	linuxTap := &linux_interfaces.Interface{
   309  		Name:        linuxTapName,
   310  		Type:        linux_interfaces.Interface_TAP_TO_VPP,
   311  		Enabled:     true,
   312  		IpAddresses: []string{"alloc:" + networkName},
   313  		HostIfName:  linuxTapHostname,
   314  		PhysAddress: linuxTapHw,
   315  		Link: &linux_interfaces.Interface_Tap{
   316  			Tap: &linux_interfaces.TapLink{
   317  				VppTapIfName: vppTapName,
   318  			},
   319  		},
   320  		Namespace: &linux_namespace.NetNamespace{
   321  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   322  			Reference: MsNamePrefix + msName,
   323  		},
   324  	}
   325  
   326  	linuxArp := &linux_l3.ARPEntry{
   327  		Interface: linuxTapName,
   328  		IpAddress: "alloc:" + networkName + "/" + vppTapName,
   329  		HwAddress: vppTapHw,
   330  	}
   331  
   332  	linuxRoute := &linux_l3.Route{
   333  		OutgoingInterface: linuxTapName,
   334  		Scope:             linux_l3.Route_GLOBAL,
   335  		DstNetwork:        "alloc:" + networkName + "/" + vppLoopName,
   336  		GwAddr:            "alloc:" + networkName + "/GW",
   337  	}
   338  
   339  	// link route is necessary to route the GW of the linux TAP interface
   340  	linuxLinkRoute := &linux_l3.Route{
   341  		OutgoingInterface: linuxTapName,
   342  		Scope:             linux_l3.Route_LINK,
   343  		DstNetwork:        "alloc:" + networkName + "/" + linuxTapName + "/GW",
   344  	}
   345  
   346  	ctx.StartMicroservice(msName)
   347  	req := ctx.GenericClient().ChangeRequest()
   348  	err := req.Update(
   349  		vppLoopAddr, vppTapAddr, linuxTapAddr,
   350  		vppLoop, vppTap, linuxTap,
   351  		linuxArp, linuxRoute, linuxLinkRoute,
   352  	).Send(context.Background())
   353  	ctx.Expect(err).ToNot(HaveOccurred())
   354  
   355  	checkItemsAreConfigured := func(msRestart, withLinkRoute bool) {
   356  		// configured immediately:
   357  		ctx.Expect(ctx.GetValueState(vppLoopAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   358  		ctx.Expect(ctx.GetValueState(vppTapAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   359  		ctx.Expect(ctx.GetValueState(linuxTapAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   360  		ctx.Expect(ctx.GetValueState(vppLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   361  		// the rest depends on the microservice
   362  		if msRestart {
   363  			ctx.Eventually(ctx.GetValueStateClb(vppTap)).Should(Equal(kvscheduler.ValueState_CONFIGURED))
   364  		} else {
   365  			ctx.Expect(ctx.GetValueState(vppTap)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   366  		}
   367  		ctx.Expect(ctx.GetValueState(linuxTap)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   368  		ctx.Expect(ctx.GetValueState(linuxArp)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   369  		if withLinkRoute {
   370  			ctx.Expect(ctx.GetValueState(linuxRoute)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   371  			ctx.Expect(ctx.GetValueState(linuxLinkRoute)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   372  		} else {
   373  			ctx.Expect(ctx.GetValueState(linuxRoute)).To(Equal(kvscheduler.ValueState_PENDING))
   374  		}
   375  	}
   376  	checkItemsAreConfigured(true, true)
   377  
   378  	// check connection with ping
   379  	ctx.Expect(ctx.PingFromVPP(linuxTapIP)).To(Succeed())
   380  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP)).To(Succeed())
   381  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   382  
   383  	// restart microservice
   384  	ctx.StopMicroservice(msName)
   385  	ctx.StartMicroservice(msName)
   386  	checkItemsAreConfigured(true, true)
   387  
   388  	// check connection with ping (few packets will get lost before tables are refreshed)
   389  	ctx.PingFromVPP(linuxTapIP)
   390  	ctx.Expect(ctx.PingFromVPP(linuxTapIP)).To(Succeed())
   391  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP)).To(Succeed())
   392  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   393  
   394  	// change IP addresses - the network items should be re-created
   395  	vppLoopAddr.Address = vppLoopIP2 + vppNetMask
   396  	vppTapAddr.Address = vppTapIP2 + vppNetMask
   397  	vppTapAddr.Gw = linuxTapIP2
   398  	linuxTapAddr.Address = linuxTapIP2 + linuxNetMask
   399  	linuxTapAddr.Gw = vppTapIP2
   400  
   401  	req = ctx.GenericClient().ChangeRequest()
   402  	err = req.Update(vppLoopAddr, vppTapAddr, linuxTapAddr).Send(context.Background())
   403  	ctx.Expect(err).ToNot(HaveOccurred())
   404  	checkItemsAreConfigured(false, true)
   405  
   406  	// check connection with ping
   407  	ctx.Expect(ctx.PingFromVPP(linuxTapIP)).NotTo(Succeed())
   408  
   409  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP)).NotTo(Succeed())
   410  	ctx.Expect(ctx.PingFromVPP(linuxTapIP2)).To(Succeed())
   411  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP2)).To(Succeed())
   412  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   413  
   414  	// remove link route - this should make the GW for linux TAP non-routable
   415  	req = ctx.GenericClient().ChangeRequest()
   416  	err = req.Delete(linuxLinkRoute).Send(context.Background())
   417  	ctx.Expect(err).ToNot(HaveOccurred())
   418  
   419  	// the route to VPP is pending
   420  	checkItemsAreConfigured(false, false)
   421  
   422  	// cannot ping anymore from any of the sides
   423  	ctx.Expect(ctx.PingFromVPP(linuxTapIP2)).NotTo(Succeed())
   424  	ctx.Expect(ctx.PingFromMs(msName, vppLoopIP2)).NotTo(Succeed())
   425  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   426  }
   427  
   428  // test IP address allocation using the netalloc plugin for VPP routes mainly.
   429  //
   430  // topology + addressing:
   431  //
   432  //	VPP tap (192.168.11.1/24) <--> Linux tap (192.168.11.2/24) <--> Linux loop (192.168.20.1/24, 10.10.10.10/32)
   433  //
   434  // topology + addressing AFTER CHANGE:
   435  //
   436  //	VPP tap (192.168.12.1/24) <--> Linux tap (192.168.12.2/24) <--> Linux loop (192.168.30.1/24, 10.10.10.10/32)
   437  func TestVPPRoutesWithNetalloc(t *testing.T) {
   438  	ctx := Setup(t)
   439  	defer ctx.Teardown()
   440  
   441  	const (
   442  		network1Name     = "net1"
   443  		network2Name     = "net2"
   444  		vppTapName       = "vpp-tap"
   445  		linuxTapName     = "linux-tap"
   446  		linuxTapHostname = "tap"
   447  		linuxLoopName    = "linux-loop"
   448  		vppTapIP         = "192.168.11.1"
   449  		vppTapIP2        = "192.168.12.1"
   450  		linuxTapIP       = "192.168.11.2"
   451  		linuxTapIP2      = "192.168.12.2"
   452  		linuxLoopNet1IP  = "192.168.20.1"
   453  		linuxLoopNet1IP2 = "192.168.30.1"
   454  		linuxLoopNet2IP  = "10.10.10.10"
   455  		net1Mask         = "/24"
   456  		net2Mask         = "/32"
   457  		msName           = "microservice1"
   458  	)
   459  
   460  	// ------- addresses:
   461  
   462  	vppTapAddr := &netalloc.IPAllocation{
   463  		NetworkName:   network1Name,
   464  		InterfaceName: vppTapName,
   465  		Address:       vppTapIP + net1Mask,
   466  		Gw:            linuxTapIP,
   467  	}
   468  
   469  	linuxTapAddr := &netalloc.IPAllocation{
   470  		NetworkName:   network1Name,
   471  		InterfaceName: linuxTapName,
   472  		Address:       linuxTapIP + net1Mask,
   473  		Gw:            vppTapIP,
   474  	}
   475  
   476  	linuxLoopNet1Addr := &netalloc.IPAllocation{
   477  		NetworkName:   network1Name,
   478  		InterfaceName: linuxLoopName,
   479  		Address:       linuxLoopNet1IP + net1Mask,
   480  	}
   481  
   482  	linuxLoopNet2Addr := &netalloc.IPAllocation{
   483  		NetworkName:   network2Name,
   484  		InterfaceName: linuxLoopName,
   485  		Address:       linuxLoopNet2IP + net2Mask,
   486  	}
   487  
   488  	// ------- network items:
   489  
   490  	vppTap := &vpp_interfaces.Interface{
   491  		Name:        vppTapName,
   492  		Type:        vpp_interfaces.Interface_TAP,
   493  		Enabled:     true,
   494  		IpAddresses: []string{"alloc:" + network1Name},
   495  		Link: &vpp_interfaces.Interface_Tap{
   496  			Tap: &vpp_interfaces.TapLink{
   497  				Version:        2,
   498  				ToMicroservice: MsNamePrefix + msName,
   499  			},
   500  		},
   501  	}
   502  
   503  	linuxTap := &linux_interfaces.Interface{
   504  		Name:        linuxTapName,
   505  		Type:        linux_interfaces.Interface_TAP_TO_VPP,
   506  		Enabled:     true,
   507  		IpAddresses: []string{"alloc:" + network1Name},
   508  		HostIfName:  linuxTapHostname,
   509  		Link: &linux_interfaces.Interface_Tap{
   510  			Tap: &linux_interfaces.TapLink{
   511  				VppTapIfName: vppTapName,
   512  			},
   513  		},
   514  		Namespace: &linux_namespace.NetNamespace{
   515  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   516  			Reference: MsNamePrefix + msName,
   517  		},
   518  	}
   519  
   520  	linuxLoop := &linux_interfaces.Interface{
   521  		Name:    linuxLoopName,
   522  		Type:    linux_interfaces.Interface_LOOPBACK,
   523  		Enabled: true,
   524  		IpAddresses: []string{
   525  			"127.0.0.1/8", "alloc:" + network1Name, "alloc:" + network2Name},
   526  		Namespace: &linux_namespace.NetNamespace{
   527  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   528  			Reference: MsNamePrefix + msName,
   529  		},
   530  	}
   531  
   532  	vppRouteLoopNet1 := &vpp_l3.Route{
   533  		OutgoingInterface: vppTapName,
   534  		DstNetwork:        "alloc:" + network1Name + "/" + linuxLoopName,
   535  		NextHopAddr:       "alloc:" + network1Name + "/GW",
   536  	}
   537  
   538  	vppRouteLoopNet2 := &vpp_l3.Route{
   539  		OutgoingInterface: vppTapName,
   540  		DstNetwork:        "alloc:" + network2Name + "/" + linuxLoopName,
   541  		NextHopAddr:       "alloc:" + network1Name + "/GW",
   542  	}
   543  
   544  	ctx.StartMicroservice(msName)
   545  	req := ctx.GenericClient().ChangeRequest()
   546  	err := req.Update(
   547  		vppTapAddr, linuxTapAddr, linuxLoopNet1Addr, linuxLoopNet2Addr,
   548  		vppTap, linuxTap, linuxLoop,
   549  		vppRouteLoopNet1, vppRouteLoopNet2,
   550  	).Send(context.Background())
   551  	ctx.Expect(err).ToNot(HaveOccurred())
   552  
   553  	checkItemsAreConfigured := func(msRestart, withLoopNet2Addr bool) {
   554  		// configured immediately:
   555  		if withLoopNet2Addr {
   556  			ctx.Expect(ctx.GetValueState(linuxLoopNet2Addr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   557  		}
   558  		ctx.Expect(ctx.GetValueState(vppTapAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   559  		ctx.Expect(ctx.GetValueState(linuxTapAddr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   560  		ctx.Expect(ctx.GetValueState(linuxLoopNet1Addr)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   561  		// the rest depends on the microservice
   562  		if msRestart {
   563  			ctx.Eventually(ctx.GetValueStateClb(vppTap)).Should(Equal(kvscheduler.ValueState_CONFIGURED))
   564  		} else {
   565  			ctx.Expect(ctx.GetValueState(vppTap)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   566  		}
   567  		ctx.Expect(ctx.GetValueState(linuxTap)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   568  		ctx.Expect(ctx.GetValueState(linuxLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   569  		ctx.Expect(ctx.GetValueState(vppRouteLoopNet1)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   570  		if withLoopNet2Addr {
   571  			ctx.Expect(ctx.GetValueState(vppRouteLoopNet2)).To(Equal(kvscheduler.ValueState_CONFIGURED))
   572  		} else {
   573  			ctx.Expect(ctx.GetValueState(vppRouteLoopNet2)).To(Equal(kvscheduler.ValueState_PENDING))
   574  		}
   575  	}
   576  	checkItemsAreConfigured(true, true)
   577  
   578  	// check connection with ping
   579  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet1IP)).To(Succeed())
   580  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet2IP)).To(Succeed())
   581  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   582  
   583  	// restart microservice
   584  	ctx.StopMicroservice(msName)
   585  	ctx.StartMicroservice(msName)
   586  	checkItemsAreConfigured(true, true)
   587  
   588  	// check connection with ping (few packets will get lost before tables are refreshed)
   589  	ctx.PingFromVPP(linuxLoopNet1IP)
   590  	ctx.PingFromVPP(linuxLoopNet2IP)
   591  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet1IP)).To(Succeed())
   592  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet2IP)).To(Succeed())
   593  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   594  
   595  	// change IP addresses - the network items should be re-created
   596  	linuxLoopNet1Addr.Address = linuxLoopNet1IP2 + net1Mask
   597  	vppTapAddr.Address = vppTapIP2 + net1Mask
   598  	vppTapAddr.Gw = linuxTapIP2
   599  	linuxTapAddr.Address = linuxTapIP2 + net1Mask
   600  	linuxTapAddr.Gw = vppTapIP2
   601  
   602  	req = ctx.GenericClient().ChangeRequest()
   603  	err = req.Update(linuxLoopNet1Addr, vppTapAddr, linuxTapAddr).Send(context.Background())
   604  	ctx.Expect(err).ToNot(HaveOccurred())
   605  	checkItemsAreConfigured(false, true)
   606  
   607  	// check connection with ping
   608  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet1IP)).NotTo(Succeed())
   609  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet1IP2)).To(Succeed())
   610  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet2IP)).To(Succeed())
   611  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   612  
   613  	// de-allocate loopback IP in net2 - the connection to that IP should not work anymore
   614  	req = ctx.GenericClient().ChangeRequest()
   615  	err = req.Delete(linuxLoopNet2Addr).Send(context.Background())
   616  	ctx.Expect(err).ToNot(HaveOccurred())
   617  
   618  	// loopback is still created but without IP and route is pending
   619  	checkItemsAreConfigured(false, false)
   620  
   621  	// can ping loop1, but cannot ping loop2
   622  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet1IP2)).To(Succeed())
   623  	ctx.Expect(ctx.PingFromVPP(linuxLoopNet2IP)).NotTo(Succeed())
   624  	ctx.Expect(ctx.AgentInSync()).To(BeTrue())
   625  }