go.ligato.io/vpp-agent/v3@v3.5.0/tests/e2e/040_bridge_domain_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  	"strconv"
    20  	"testing"
    21  
    22  	. "github.com/onsi/gomega"
    23  	"github.com/onsi/gomega/types"
    24  
    25  	"go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler"
    26  	linux_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    27  	linux_namespace "go.ligato.io/vpp-agent/v3/proto/ligato/linux/namespace"
    28  	vpp_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    29  	vpp_l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2"
    30  	. "go.ligato.io/vpp-agent/v3/tests/e2e/e2etest"
    31  )
    32  
    33  func bridgeDomains(ctx *TestCtx) ([]map[string]string, error) {
    34  	stdout, err := ctx.ExecVppctl("show", "bridge-domain")
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return ParseVPPTable(stdout), nil
    39  }
    40  
    41  func bdAgeIs(min int) types.GomegaMatcher {
    42  	if min == 0 {
    43  		return HaveKeyWithValue("Age(min)", "off")
    44  	}
    45  	return HaveKeyWithValue("Age(min)", strconv.Itoa(min))
    46  }
    47  
    48  func bdWithFlooding() types.GomegaMatcher {
    49  	return And(
    50  		HaveKeyWithValue("UU-Flood", "flood"),
    51  		HaveKeyWithValue("Flooding", "on"))
    52  }
    53  
    54  func bdWithForwarding() types.GomegaMatcher {
    55  	return HaveKeyWithValue("U-Forwrd", "on")
    56  }
    57  
    58  func bdWithLearning() types.GomegaMatcher {
    59  	return HaveKeyWithValue("Learning", "on")
    60  }
    61  
    62  // connect microservices into the same L2 network segment via bridge domain
    63  // and TAP interfaces.
    64  func TestBridgeDomainWithTAPs(t *testing.T) {
    65  	ctx := Setup(t)
    66  	defer ctx.Teardown()
    67  
    68  	const (
    69  		vppTap1Name       = "vpp-tap1"
    70  		linuxTap1Name     = "linux-tap1"
    71  		linuxTap1Hostname = "tap"
    72  		linuxTap1IP       = "192.168.1.2"
    73  
    74  		vppTap2Name       = "vpp-tap2"
    75  		linuxTap2Name     = "linux-tap2"
    76  		linuxTap2Hostname = "tap"
    77  		linuxTap2IP       = "192.168.1.3"
    78  
    79  		vppLoopbackName = "loop1"
    80  		vppLoopbackIP   = "192.168.1.1"
    81  
    82  		netMask = "/24"
    83  		ms1Name = "microservice1"
    84  		ms2Name = "microservice2"
    85  		bdName  = "my-bd"
    86  	)
    87  
    88  	vppTap1 := &vpp_interfaces.Interface{
    89  		Name:    vppTap1Name,
    90  		Type:    vpp_interfaces.Interface_TAP,
    91  		Enabled: true,
    92  		Link: &vpp_interfaces.Interface_Tap{
    93  			Tap: &vpp_interfaces.TapLink{
    94  				Version:        2,
    95  				ToMicroservice: MsNamePrefix + ms1Name,
    96  			},
    97  		},
    98  	}
    99  	linuxTap1 := &linux_interfaces.Interface{
   100  		Name:        linuxTap1Name,
   101  		Type:        linux_interfaces.Interface_TAP_TO_VPP,
   102  		Enabled:     true,
   103  		IpAddresses: []string{linuxTap1IP + netMask},
   104  		HostIfName:  linuxTap1Hostname,
   105  		Link: &linux_interfaces.Interface_Tap{
   106  			Tap: &linux_interfaces.TapLink{
   107  				VppTapIfName: vppTap1Name,
   108  			},
   109  		},
   110  		Namespace: &linux_namespace.NetNamespace{
   111  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   112  			Reference: MsNamePrefix + ms1Name,
   113  		},
   114  	}
   115  
   116  	vppTap2 := &vpp_interfaces.Interface{
   117  		Name:    vppTap2Name,
   118  		Type:    vpp_interfaces.Interface_TAP,
   119  		Enabled: true,
   120  		Link: &vpp_interfaces.Interface_Tap{
   121  			Tap: &vpp_interfaces.TapLink{
   122  				Version:        2,
   123  				ToMicroservice: MsNamePrefix + ms2Name,
   124  			},
   125  		},
   126  	}
   127  	linuxTap2 := &linux_interfaces.Interface{
   128  		Name:        linuxTap2Name,
   129  		Type:        linux_interfaces.Interface_TAP_TO_VPP,
   130  		Enabled:     true,
   131  		IpAddresses: []string{linuxTap2IP + netMask},
   132  		HostIfName:  linuxTap2Hostname,
   133  		Link: &linux_interfaces.Interface_Tap{
   134  			Tap: &linux_interfaces.TapLink{
   135  				VppTapIfName: vppTap2Name,
   136  			},
   137  		},
   138  		Namespace: &linux_namespace.NetNamespace{
   139  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   140  			Reference: MsNamePrefix + ms2Name,
   141  		},
   142  	}
   143  
   144  	vppLoop := &vpp_interfaces.Interface{
   145  		Name:        vppLoopbackName,
   146  		Type:        vpp_interfaces.Interface_SOFTWARE_LOOPBACK,
   147  		Enabled:     true,
   148  		IpAddresses: []string{vppLoopbackIP + netMask},
   149  	}
   150  
   151  	bd := &vpp_l2.BridgeDomain{
   152  		Name:                bdName,
   153  		Flood:               true,
   154  		Forward:             true,
   155  		Learn:               true,
   156  		UnknownUnicastFlood: true,
   157  		Interfaces: []*vpp_l2.BridgeDomain_Interface{
   158  			{
   159  				Name: vppTap1Name,
   160  			},
   161  			{
   162  				Name: vppTap2Name,
   163  			},
   164  			{
   165  				Name:                    vppLoopbackName,
   166  				SplitHorizonGroup:       1,
   167  				BridgedVirtualInterface: true,
   168  			},
   169  		},
   170  	}
   171  
   172  	ctx.StartMicroservice(ms1Name)
   173  	ctx.StartMicroservice(ms2Name)
   174  	req := ctx.GenericClient().ChangeRequest()
   175  	err := req.Update(
   176  		vppTap1,
   177  		linuxTap1,
   178  		vppTap2,
   179  		linuxTap2,
   180  		vppLoop,
   181  		bd,
   182  	).Send(context.Background())
   183  	ctx.Expect(err).To(BeNil(), "Transaction creating BD with TAPs failed")
   184  
   185  	ctx.Expect(ctx.GetValueState(vppLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   186  		"BD BVI should be configured even before microservices start")
   187  	ctx.Eventually(ctx.GetValueStateClb(vppTap1)).Should(Equal(kvscheduler.ValueState_CONFIGURED),
   188  		"TAP attached to a newly started microservice1 should be eventually configured")
   189  	ctx.Eventually(ctx.GetValueStateClb(vppTap2)).Should(Equal(kvscheduler.ValueState_CONFIGURED),
   190  		"TAP attached to a newly started microservice2 should be eventually configured")
   191  
   192  	if ctx.VppRelease() < "21.01" { // "show bridge-domain" hard to parse in VPP>=21.01 (https://jira.fd.io/browse/VPP-1969)
   193  		bds, err := bridgeDomains(ctx)
   194  		ctx.Expect(err).ToNot(HaveOccurred())
   195  		ctx.Expect(bds).To(HaveLen(1))
   196  		ctx.Expect(bds[0]).To(SatisfyAll(
   197  			bdAgeIs(0), bdWithFlooding(), bdWithForwarding(), bdWithLearning()))
   198  	}
   199  
   200  	ctx.Expect(ctx.PingFromMs(ms2Name, linuxTap1IP)).To(Succeed())
   201  	ctx.Expect(ctx.PingFromMs(ms1Name, linuxTap2IP)).To(Succeed())
   202  	ctx.Expect(ctx.PingFromMs(ms1Name, vppLoopbackIP)).To(Succeed())
   203  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   204  	ctx.Expect(ctx.PingFromVPP(linuxTap1IP)).To(Succeed())
   205  	ctx.Expect(ctx.PingFromVPP(linuxTap2IP)).To(Succeed())
   206  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   207  
   208  	// kill one of the microservices
   209  	// - "Eventually" is also used with linuxTap1 to wait for retry txn that
   210  	//   will change state from RETRYING to PENDING
   211  	ctx.StopMicroservice(ms1Name)
   212  	ctx.Eventually(ctx.GetValueStateClb(vppTap1)).Should(Equal(kvscheduler.ValueState_PENDING),
   213  		"Without microservice, the associated VPP-TAP should be pending")
   214  	ctx.Eventually(ctx.GetValueStateClb(linuxTap1)).Should(Equal(kvscheduler.ValueState_PENDING),
   215  		"Without microservice, the associated LinuxTAP should be pending")
   216  	ctx.Expect(ctx.GetValueState(vppTap2)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   217  		"VPP-TAP attached to running microservice is not configured")
   218  	ctx.Expect(ctx.GetValueState(linuxTap2)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   219  		"Linux-TAP attached to running microservice is not configured")
   220  	ctx.Expect(ctx.GetValueState(vppLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   221  		"BD BVI interface is not configured")
   222  	ctx.Expect(ctx.GetValueState(bd)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   223  		"BD is not configured")
   224  
   225  	ctx.Expect(ctx.PingFromMs(ms2Name, linuxTap1IP)).ToNot(Succeed())
   226  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   227  	ctx.Expect(ctx.PingFromVPP(linuxTap1IP)).ToNot(Succeed())
   228  	ctx.Expect(ctx.PingFromVPP(linuxTap2IP)).To(Succeed())
   229  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   230  
   231  	// restart the microservice
   232  	ctx.StartMicroservice(ms1Name)
   233  	ctx.Eventually(ctx.GetValueStateClb(vppTap1)).Should(Equal(kvscheduler.ValueState_CONFIGURED),
   234  		"VPP-TAP attached to a re-started microservice1 should be eventually configured")
   235  	ctx.Expect(ctx.GetValueState(linuxTap1)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   236  		"Linux-TAP attached to a re-started microservice1 is not configured")
   237  
   238  	// Waiting for TAP interface after restart
   239  	// See: https://github.com/ligato/vpp-agent/issues/1489
   240  	ctx.Eventually(ctx.PingFromMsClb(ms2Name, linuxTap1IP), "18s", "2s").Should(Succeed())
   241  	ctx.Expect(ctx.PingFromMs(ms1Name, linuxTap2IP)).To(Succeed())
   242  	ctx.Expect(ctx.PingFromMs(ms1Name, vppLoopbackIP)).To(Succeed())
   243  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   244  	ctx.Expect(ctx.PingFromVPP(linuxTap1IP)).To(Succeed())
   245  	ctx.Expect(ctx.PingFromVPP(linuxTap2IP)).To(Succeed())
   246  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   247  
   248  	// change bridge domain config to trigger re-creation
   249  	bd.MacAge = 10
   250  	req = ctx.GenericClient().ChangeRequest()
   251  	err = req.Update(
   252  		bd,
   253  	).Send(context.Background())
   254  	ctx.Expect(err).ToNot(HaveOccurred(), "Transaction updating BD failed")
   255  
   256  	ctx.Expect(ctx.PingFromMs(ms2Name, linuxTap1IP)).To(Succeed())
   257  	ctx.Expect(ctx.PingFromMs(ms1Name, linuxTap2IP)).To(Succeed())
   258  	ctx.Expect(ctx.PingFromMs(ms1Name, vppLoopbackIP)).To(Succeed())
   259  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   260  	ctx.Expect(ctx.PingFromVPP(linuxTap1IP)).To(Succeed())
   261  	ctx.Expect(ctx.PingFromVPP(linuxTap2IP)).To(Succeed())
   262  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   263  
   264  	if ctx.VppRelease() < "21.01" { // "show bridge-domain" hard to parse in VPP>=21.01 (https://jira.fd.io/browse/VPP-1969)
   265  		bds, err := bridgeDomains(ctx)
   266  		ctx.Expect(err).ToNot(HaveOccurred())
   267  		ctx.Expect(bds).To(HaveLen(1))
   268  		ctx.Expect(bds[0]).To(SatisfyAll(
   269  			bdAgeIs(10), bdWithFlooding(), bdWithForwarding(), bdWithLearning()))
   270  	}
   271  }
   272  
   273  // connect microservices into the same L2 network segment via bridge domain
   274  // and AF-PACKET+VETH interfaces.
   275  func TestBridgeDomainWithAfPackets(t *testing.T) {
   276  	ctx := Setup(t)
   277  	defer ctx.Teardown()
   278  
   279  	const (
   280  		afPacket1Name  = "vpp-afpacket1"
   281  		veth1AName     = "vpp-veth-1a"
   282  		veth1BName     = "vpp-veth-1b"
   283  		veth1AHostname = "veth1a"
   284  		veth1BHostname = "veth1b"
   285  		veth1IP        = "192.168.1.2"
   286  
   287  		afPacket2Name  = "vpp-afpacket2"
   288  		veth2AName     = "vpp-veth-2a"
   289  		veth2BName     = "vpp-veth-2b"
   290  		veth2AHostname = "veth2a"
   291  		veth2BHostname = "veth2b"
   292  		veth2IP        = "192.168.1.3"
   293  
   294  		vppLoopbackName = "loop1"
   295  		vppLoopbackIP   = "192.168.1.1"
   296  
   297  		netMask = "/24"
   298  		ms1Name = "microservice1"
   299  		ms2Name = "microservice2"
   300  		bdName  = "my-bd"
   301  	)
   302  
   303  	afPacket1 := &vpp_interfaces.Interface{
   304  		Name:    afPacket1Name,
   305  		Type:    vpp_interfaces.Interface_AF_PACKET,
   306  		Enabled: true,
   307  		Link: &vpp_interfaces.Interface_Afpacket{
   308  			Afpacket: &vpp_interfaces.AfpacketLink{
   309  				HostIfName: veth1BHostname,
   310  			},
   311  		},
   312  	}
   313  
   314  	veth1a := &linux_interfaces.Interface{
   315  		Name:        veth1AName,
   316  		Type:        linux_interfaces.Interface_VETH,
   317  		Enabled:     true,
   318  		HostIfName:  veth1AHostname,
   319  		IpAddresses: []string{veth1IP + netMask},
   320  		Link: &linux_interfaces.Interface_Veth{
   321  			Veth: &linux_interfaces.VethLink{
   322  				PeerIfName: veth1BName,
   323  			},
   324  		},
   325  		Namespace: &linux_namespace.NetNamespace{
   326  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   327  			Reference: MsNamePrefix + ms1Name,
   328  		},
   329  	}
   330  
   331  	veth1b := &linux_interfaces.Interface{
   332  		Name:       veth1BName,
   333  		Type:       linux_interfaces.Interface_VETH,
   334  		Enabled:    true,
   335  		HostIfName: veth1BHostname,
   336  		Link: &linux_interfaces.Interface_Veth{
   337  			Veth: &linux_interfaces.VethLink{
   338  				PeerIfName: veth1AName,
   339  			},
   340  		},
   341  	}
   342  
   343  	afPacket2 := &vpp_interfaces.Interface{
   344  		Name:    afPacket2Name,
   345  		Type:    vpp_interfaces.Interface_AF_PACKET,
   346  		Enabled: true,
   347  		Link: &vpp_interfaces.Interface_Afpacket{
   348  			Afpacket: &vpp_interfaces.AfpacketLink{
   349  				HostIfName: veth2BHostname,
   350  			},
   351  		},
   352  	}
   353  
   354  	veth2a := &linux_interfaces.Interface{
   355  		Name:        veth2AName,
   356  		Type:        linux_interfaces.Interface_VETH,
   357  		Enabled:     true,
   358  		HostIfName:  veth2AHostname,
   359  		IpAddresses: []string{veth2IP + netMask},
   360  		Link: &linux_interfaces.Interface_Veth{
   361  			Veth: &linux_interfaces.VethLink{
   362  				PeerIfName: veth2BName,
   363  			},
   364  		},
   365  		Namespace: &linux_namespace.NetNamespace{
   366  			Type:      linux_namespace.NetNamespace_MICROSERVICE,
   367  			Reference: MsNamePrefix + ms2Name,
   368  		},
   369  	}
   370  
   371  	veth2b := &linux_interfaces.Interface{
   372  		Name:       veth2BName,
   373  		Type:       linux_interfaces.Interface_VETH,
   374  		Enabled:    true,
   375  		HostIfName: veth2BHostname,
   376  		Link: &linux_interfaces.Interface_Veth{
   377  			Veth: &linux_interfaces.VethLink{
   378  				PeerIfName: veth2AName,
   379  			},
   380  		},
   381  	}
   382  
   383  	vppLoop := &vpp_interfaces.Interface{
   384  		Name:        vppLoopbackName,
   385  		Type:        vpp_interfaces.Interface_SOFTWARE_LOOPBACK,
   386  		Enabled:     true,
   387  		IpAddresses: []string{vppLoopbackIP + netMask},
   388  	}
   389  
   390  	bd := &vpp_l2.BridgeDomain{
   391  		Name:                bdName,
   392  		Flood:               true,
   393  		Forward:             true,
   394  		Learn:               true,
   395  		UnknownUnicastFlood: true,
   396  		Interfaces: []*vpp_l2.BridgeDomain_Interface{
   397  			{
   398  				Name: afPacket1Name,
   399  			},
   400  			{
   401  				Name: afPacket2Name,
   402  			},
   403  			{
   404  				Name:                    vppLoopbackName,
   405  				SplitHorizonGroup:       1,
   406  				BridgedVirtualInterface: true,
   407  			},
   408  		},
   409  	}
   410  
   411  	ctx.StartMicroservice(ms1Name)
   412  	ctx.StartMicroservice(ms2Name)
   413  	req := ctx.GenericClient().ChangeRequest()
   414  	err := req.Update(
   415  		afPacket1,
   416  		veth1a, veth1b,
   417  		afPacket2,
   418  		veth2a, veth2b,
   419  		vppLoop,
   420  		bd,
   421  	).Send(context.Background())
   422  	ctx.Expect(err).ToNot(HaveOccurred(), "Transaction creating BD with AF-PACKETs failed")
   423  
   424  	ctx.Expect(ctx.GetValueState(vppLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   425  		"BD BVI should be configured even before microservices start")
   426  	ctx.Eventually(ctx.GetValueStateClb(afPacket1)).Should(Equal(kvscheduler.ValueState_CONFIGURED),
   427  		"AF-PACKET attached to a newly started microservice1 should be eventually configured")
   428  	ctx.Eventually(ctx.GetValueStateClb(afPacket2)).Should(Equal(kvscheduler.ValueState_CONFIGURED),
   429  		"AF-PACKET attached to a newly started microservice2 should be eventually configured")
   430  
   431  	if ctx.VppRelease() < "21.01" { // "show bridge-domain" hard to parse in VPP>=21.01 (https://jira.fd.io/browse/VPP-1969)
   432  		bds, err := bridgeDomains(ctx)
   433  		ctx.Expect(err).ToNot(HaveOccurred())
   434  		ctx.Expect(bds).To(HaveLen(1))
   435  		ctx.Expect(bds[0]).To(SatisfyAll(
   436  			bdAgeIs(0), bdWithFlooding(), bdWithForwarding(), bdWithLearning()))
   437  	}
   438  
   439  	ctx.Expect(ctx.PingFromMs(ms2Name, veth1IP)).To(Succeed())
   440  	ctx.Expect(ctx.PingFromMs(ms1Name, veth2IP)).To(Succeed())
   441  	ctx.Expect(ctx.PingFromMs(ms1Name, vppLoopbackIP)).To(Succeed())
   442  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   443  	ctx.Expect(ctx.PingFromVPP(veth1IP)).To(Succeed())
   444  	ctx.Expect(ctx.PingFromVPP(veth2IP)).To(Succeed())
   445  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   446  
   447  	// kill one of the microservices
   448  	// - both AF-PACKET and VETH use separate "Eventually" assertion since
   449  	//   they react to different SB notifications
   450  	ctx.StopMicroservice(ms1Name)
   451  	ctx.Eventually(ctx.GetValueStateClb(afPacket1)).Should(Equal(kvscheduler.ValueState_PENDING),
   452  		"Without microservice, the associated AF-PACKET should be pending")
   453  	ctx.Eventually(ctx.GetValueStateClb(veth1a)).Should(Equal(kvscheduler.ValueState_PENDING),
   454  		"Without microservice, the associated VETH should be pending")
   455  	ctx.Expect(ctx.GetValueState(veth1b)).To(Equal(kvscheduler.ValueState_PENDING),
   456  		"Without microservice, the associated VETH should be pending")
   457  	ctx.Expect(ctx.GetValueState(afPacket2)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   458  		"AF-PACKET attached to running microservice is not configured")
   459  	ctx.Expect(ctx.GetValueState(veth2a)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   460  		"VETH attached to running microservice is not configured")
   461  	ctx.Expect(ctx.GetValueState(veth2b)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   462  		"VETH attached to running microservice is not configured")
   463  	ctx.Expect(ctx.GetValueState(vppLoop)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   464  		"BD BVI interface is not configured")
   465  	ctx.Expect(ctx.GetValueState(bd)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   466  		"BD is not configured")
   467  
   468  	ctx.Expect(ctx.PingFromMs(ms2Name, veth1IP)).ToNot(Succeed())
   469  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   470  	ctx.Expect(ctx.PingFromVPP(veth1IP)).ToNot(Succeed())
   471  	ctx.Expect(ctx.PingFromVPP(veth2IP)).To(Succeed())
   472  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   473  
   474  	// restart the microservice
   475  	ctx.StartMicroservice(ms1Name)
   476  	ctx.Eventually(ctx.GetValueStateClb(afPacket1)).Should(Equal(kvscheduler.ValueState_CONFIGURED),
   477  		"AF-PACKET attached to a re-started microservice1 should be eventually configured")
   478  	ctx.Expect(ctx.GetValueState(veth1a)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   479  		"VETH attached to re-started microservice1 is not configured")
   480  	ctx.Expect(ctx.GetValueState(veth1b)).To(Equal(kvscheduler.ValueState_CONFIGURED),
   481  		"VETH attached to re-started microservice1 is not configured")
   482  
   483  	// Waiting for AF-PACKET interface after restart
   484  	// See: https://github.com/ligato/vpp-agent/issues/1489
   485  	ctx.Eventually(ctx.PingFromMsClb(ms2Name, veth1IP), "18s", "2s").Should(Succeed())
   486  	ctx.Expect(ctx.PingFromMs(ms1Name, veth2IP)).To(Succeed())
   487  	ctx.Expect(ctx.PingFromMs(ms1Name, vppLoopbackIP)).To(Succeed())
   488  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   489  	ctx.Expect(ctx.PingFromVPP(veth1IP)).To(Succeed())
   490  	ctx.Expect(ctx.PingFromVPP(veth2IP)).To(Succeed())
   491  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   492  
   493  	// change bridge domain config to trigger re-creation
   494  	bd.MacAge = 10
   495  	req = ctx.GenericClient().ChangeRequest()
   496  	err = req.Update(
   497  		bd,
   498  	).Send(context.Background())
   499  	ctx.Expect(err).ToNot(HaveOccurred(), "Transaction updating BD failed")
   500  
   501  	if ctx.VppRelease() < "21.01" { // "show bridge-domain" hard to parse in VPP>=21.01 (https://jira.fd.io/browse/VPP-1969)
   502  		bds, err := bridgeDomains(ctx)
   503  		ctx.Expect(err).ToNot(HaveOccurred())
   504  		ctx.Expect(bds).To(HaveLen(1))
   505  		ctx.Expect(bds[0]).To(SatisfyAll(
   506  			bdAgeIs(10), bdWithFlooding(), bdWithForwarding(), bdWithLearning()))
   507  	}
   508  
   509  	ctx.Expect(ctx.PingFromMs(ms2Name, veth1IP)).To(Succeed())
   510  	ctx.Expect(ctx.PingFromMs(ms1Name, veth2IP)).To(Succeed())
   511  	ctx.Expect(ctx.PingFromMs(ms1Name, vppLoopbackIP)).To(Succeed())
   512  	ctx.Expect(ctx.PingFromMs(ms2Name, vppLoopbackIP)).To(Succeed())
   513  	ctx.Expect(ctx.PingFromVPP(veth1IP)).To(Succeed())
   514  	ctx.Expect(ctx.PingFromVPP(veth2IP)).To(Succeed())
   515  	ctx.Expect(ctx.AgentInSync()).To(BeTrue(), "Agent is not in-sync")
   516  }