ae757564dc6ec01b371ae3834fb2fb11690f88b6
[netvirt.git] / cloud-servicechain / impl / src / test / java / org / opendaylight / netvirt / cloudservicechain / VPNServiceChainHandlerTest.java
1 /*
2  * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.cloudservicechain;
9
10 import static org.mockito.Matchers.anyObject;
11 import static org.mockito.Matchers.argThat;
12 import static org.mockito.Matchers.eq;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.never;
15 import static org.mockito.Mockito.times;
16 import static org.mockito.Mockito.verify;
17 import static org.mockito.Mockito.when;
18
19 import com.google.common.base.Optional;
20 import com.google.common.util.concurrent.CheckedFuture;
21 import java.math.BigInteger;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.stream.Collectors;
25 import org.hamcrest.Description;
26 import org.hamcrest.Matcher;
27 import org.hamcrest.TypeSafeMatcher;
28 import org.junit.After;
29 import org.junit.AfterClass;
30 import org.junit.Before;
31 import org.junit.BeforeClass;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.mockito.ArgumentCaptor;
35 import org.mockito.Mock;
36 import org.mockito.runners.MockitoJUnitRunner;
37 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
38 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
39 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
40 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
41 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
42 import org.opendaylight.genius.interfacemanager.globals.InterfaceServiceUtil;
43 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
44 import org.opendaylight.genius.mdsalutil.FlowEntity;
45 import org.opendaylight.genius.mdsalutil.NwConstants;
46 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
47 import org.opendaylight.infrautils.jobcoordinator.internal.JobCoordinatorImpl;
48 import org.opendaylight.infrautils.metrics.MetricProvider;
49 import org.opendaylight.infrautils.metrics.testimpl.TestMetricProviderImpl;
50 import org.opendaylight.netvirt.cloudservicechain.matchers.FlowEntityMatcher;
51 import org.opendaylight.netvirt.cloudservicechain.matchers.FlowMatcher;
52 import org.opendaylight.netvirt.cloudservicechain.utils.VpnServiceChainUtils;
53 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
54 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
55 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceKey;
79 import org.opendaylight.yangtools.yang.binding.DataObject;
80 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
83
84
85 @RunWith(MockitoJUnitRunner.class)
86 public class VPNServiceChainHandlerTest {
87
88     static final Logger LOG = LoggerFactory.getLogger(VPNServiceChainHandler.class);
89
90     static final String RD = "100:100";
91     static final String VPN_NAME = "AccessVPN";
92     static final long VPN_ID = 1;
93     static final long SCF_TAG = 1L;
94     static final int SERV_CHAIN_TAG = 100;
95     static final BigInteger DPN_ID = BigInteger.valueOf(1L);
96     static final int LPORT_TAG = 1;
97     static final String DC_GW_IP = "3.3.3.3";
98
99     private static MetricProvider metricProvider = new TestMetricProviderImpl();
100
101     private static JobCoordinatorImpl jobCoordinator = new JobCoordinatorImpl(metricProvider);
102
103     private VPNServiceChainHandler vpnsch; // SUT
104
105     @Mock DataBroker broker;
106     @Mock ReadOnlyTransaction readTx;
107     @Mock WriteTransaction writeTx;
108     @Mock IMdsalApiManager mdsalMgr;
109     @Mock IVpnFootprintService vpnFootprintService;
110     @Mock IInterfaceManager ifaceMgr;
111
112     @BeforeClass
113     public static void setUpBeforeClass() throws Exception {
114     }
115
116     @AfterClass
117     public static void tearDownAfterClass() throws Exception {
118         jobCoordinator.destroy();
119     }
120
121     @Before
122     public void setUp() throws Exception {
123
124         when(broker.newReadOnlyTransaction()).thenReturn(readTx);
125         when(broker.newWriteOnlyTransaction()).thenReturn(writeTx);
126         CheckedFuture chkdFuture = mock(CheckedFuture.class);
127         when(writeTx.submit()).thenReturn(chkdFuture);
128
129         // SUT
130         vpnsch = new VPNServiceChainHandler(broker, mdsalMgr, vpnFootprintService, ifaceMgr, jobCoordinator);
131     }
132
133     @After
134     public void tearDown() throws Exception {
135     }
136
137     private <T extends DataObject> Matcher<InstanceIdentifier<T>> isIIdType(final Class<T> klass) {
138         return new TypeSafeMatcher<InstanceIdentifier<T>>() {
139             @Override
140             public void describeTo(Description desc) {
141                 desc.appendText("Instance Identifier should have Target Type " + klass);
142             }
143
144             @Override
145             protected boolean matchesSafely(InstanceIdentifier<T> id) {
146                 return id.getTargetType().equals(klass);
147             }
148         };
149     }
150
151     private void stubGetRouteDistinguisher(String vpnName, String rd) throws Exception {
152         VpnInstance instance = new VpnInstanceBuilder().setKey(new VpnInstanceKey(vpnName)).setVrfId(rd)
153                                                        .setVpnInstanceName(vpnName).build();
154
155         InstanceIdentifier<VpnInstance> id = VpnServiceChainUtils.getVpnInstanceToVpnIdIdentifier(vpnName);
156         CheckedFuture chkdFuture = mock(CheckedFuture.class);
157
158         when(chkdFuture.checkedGet()).thenReturn(Optional.of(instance));
159         // when(readTx.read(eq(LogicalDatastoreType.CONFIGURATION), eq(id))).thenReturn(chkdFuture);
160         when(readTx.read(eq(LogicalDatastoreType.CONFIGURATION),
161                          argThat(isIIdType(VpnInstance.class)))).thenReturn(chkdFuture);
162     }
163
164
165     private void stubNoRdForVpnName(String vpnName) throws Exception {
166         CheckedFuture<Optional<VpnInstance>, ReadFailedException> chkdFuture = mock(CheckedFuture.class);
167         when(chkdFuture.checkedGet()).thenReturn(Optional.absent());
168         when(readTx.read(eq(LogicalDatastoreType.CONFIGURATION),
169                          eq(VpnServiceChainUtils.getVpnInstanceToVpnIdIdentifier(vpnName))))
170             .thenReturn(chkdFuture);
171     }
172
173     private void stubNoVpnInstanceForRD(String rd) throws Exception {
174         CheckedFuture<Optional<VpnInstanceOpDataEntry>, ReadFailedException> chkdFuture = mock(CheckedFuture.class);
175         when(chkdFuture.checkedGet()).thenReturn(Optional.absent());
176
177         InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class)
178                 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
179
180         when(readTx.read(eq(LogicalDatastoreType.OPERATIONAL), eq(id))).thenReturn(chkdFuture);
181     }
182
183     private void stubGetVpnInstance(String rd, String ipAddress, String ifaceName) throws Exception {
184
185         IpAddresses ipAddr =
186             new IpAddressesBuilder().setIpAddress(ipAddress).setKey(new IpAddressesKey(ipAddress)).build();
187         List<VpnInterfaces> ifacesList =
188             Collections.singletonList(new VpnInterfacesBuilder().setInterfaceName(ifaceName).build());
189         VpnToDpnListBuilder vtdlb =
190             new VpnToDpnListBuilder().setKey(new VpnToDpnListKey(DPN_ID))
191                                      .setDpnId(DPN_ID)
192                                      .setIpAddresses(Collections.singletonList(ipAddr))
193                                      .setVpnInterfaces(ifacesList);
194
195         VpnInstanceOpDataEntry vpnInstanceOpDataEntry =
196             new VpnInstanceOpDataEntryBuilder().setKey(new VpnInstanceOpDataEntryKey(rd))
197                                                .setVpnId(VPN_ID)
198                                                .setVpnToDpnList(Collections.singletonList(vtdlb.build()))
199                                                .setVrfId("1").build();
200         CheckedFuture chkdFuture = mock(CheckedFuture.class);
201         when(chkdFuture.checkedGet()).thenReturn(Optional.of(vpnInstanceOpDataEntry));
202         when(readTx.read(eq(LogicalDatastoreType.OPERATIONAL),
203                          eq(VpnServiceChainUtils.getVpnInstanceOpDataIdentifier(rd)))).thenReturn(chkdFuture);
204     }
205
206     private void stubGetVrfEntries(String rd, List<VrfEntry> vrfEntryList)
207         throws Exception {
208
209         VrfTables tables = new VrfTablesBuilder().setKey(new VrfTablesKey(rd)).setRouteDistinguisher(rd)
210                                                  .setVrfEntry(vrfEntryList).build();
211         CheckedFuture chkdFuture = mock(CheckedFuture.class);
212         when(chkdFuture.checkedGet()).thenReturn(Optional.of(tables));
213         when(readTx.read(eq(LogicalDatastoreType.CONFIGURATION), eq(VpnServiceChainUtils.buildVrfId(rd))))
214                 .thenReturn(chkdFuture);
215
216     }
217
218     private void stubReadVpnToDpnList(String rd, BigInteger dpnId, List<String> vpnIfacesOnDpn)
219         throws Exception {
220
221         List<VpnInterfaces> vpnIfacesList =
222             vpnIfacesOnDpn.stream()
223                           .map((ifaceName) -> new VpnInterfacesBuilder().setKey(new VpnInterfacesKey(ifaceName))
224                                                                         .setInterfaceName(ifaceName).build())
225                           .collect(Collectors.toList());
226
227         CheckedFuture chkdFuture = mock(CheckedFuture.class);
228         when(chkdFuture.checkedGet()).thenReturn(Optional.of(vpnIfacesList));
229         when(readTx.read(eq(LogicalDatastoreType.OPERATIONAL),
230                          eq(VpnServiceChainUtils.getVpnToDpnListIdentifier(rd, dpnId))))
231              .thenReturn(chkdFuture);
232     }
233
234     private void stubScfIsBoundOnIface(long scfTag, String ifName) throws Exception {
235         CheckedFuture chkdFuture = mock(CheckedFuture.class);
236         BoundServices boundService =
237             InterfaceServiceUtil.getBoundServices(ifName, NwConstants.SCF_SERVICE_INDEX,
238                                                   CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY,
239                                                   CloudServiceChainConstants.COOKIE_SCF_BASE,
240                                                   null /*instructions*/);
241
242         when(chkdFuture.checkedGet()).thenReturn(Optional.of(boundService));
243         when(readTx.read(eq(LogicalDatastoreType.CONFIGURATION),
244                          eq(VpnServiceChainUtils.buildBoundServicesIid(NwConstants.SCF_SERVICE_INDEX, ifName))))
245              .thenReturn(chkdFuture);
246     }
247
248     private void stubScfIsNotBoundOnIface(long scfTag, String ifName) throws Exception {
249         CheckedFuture chkdFuture = mock(CheckedFuture.class);
250         when(chkdFuture.checkedGet()).thenReturn(Optional.absent());
251         when(readTx.read(eq(LogicalDatastoreType.CONFIGURATION),
252                          eq(VpnServiceChainUtils.buildBoundServicesIid(NwConstants.SCF_SERVICE_INDEX, ifName))))
253              .thenReturn(chkdFuture);
254     }
255
256     @Test
257     public void testprogramScfToVpnPipelineNullRd() throws Exception {
258         /////////////////////
259         // Basic stubbing //
260         /////////////////////
261         stubNoRdForVpnName(VPN_NAME);
262         /////////////////////
263         // SUT //
264         /////////////////////
265         vpnsch.programScfToVpnPipeline(VPN_NAME, SCF_TAG, SERV_CHAIN_TAG, DPN_ID.longValue(), LPORT_TAG,
266                                        /* lastServiceChain */ false,
267                                        NwConstants.ADD_FLOW);
268         // verify that nothing is written in Open Flow tables
269
270         ArgumentCaptor<FlowEntity> argumentCaptor = ArgumentCaptor.forClass(FlowEntity.class);
271         verify(mdsalMgr, times(0)).installFlow(argumentCaptor.capture());
272
273         List<FlowEntity> installedFlowsCaptured = argumentCaptor.getAllValues();
274         assert installedFlowsCaptured.isEmpty();
275
276     }
277
278     @Test
279     public void testprogramScfToVpnPipelineNullVpnInstance() throws Exception {
280
281         /////////////////////
282         // Basic stubbing //
283         /////////////////////
284         stubGetRouteDistinguisher(VPN_NAME, RD);
285         stubNoVpnInstanceForRD(RD);
286         /////////////////////
287         // SUT //
288         /////////////////////
289         vpnsch.programScfToVpnPipeline(VPN_NAME, SCF_TAG, SERV_CHAIN_TAG, DPN_ID.longValue(), LPORT_TAG,
290                                        /* lastServiceChain */ false, NwConstants.ADD_FLOW);
291
292         ArgumentCaptor<FlowEntity> argumentCaptor = ArgumentCaptor.forClass(FlowEntity.class);
293         verify(mdsalMgr, times(0)).installFlow(argumentCaptor.capture());
294
295         List<FlowEntity> installedFlowsCaptured = argumentCaptor.getAllValues();
296         assert installedFlowsCaptured.isEmpty();
297
298     }
299
300     @Test
301     public void testprogramScfToVpnPipeline() throws Exception {
302
303         /////////////////////
304         // Basic stubbing //
305         /////////////////////
306         stubGetRouteDistinguisher(VPN_NAME, RD);
307         stubGetVpnInstance(RD, "1.2.3.4", "eth0");
308         VrfEntry vrfEntry = FibHelper.getVrfEntryBuilder("11.12.13.14", 2000L, DC_GW_IP, RouteOrigin.STATIC, null)
309                 .build();
310         stubGetVrfEntries(RD, Collections.singletonList(vrfEntry));
311         stubReadVpnToDpnList(RD, DPN_ID, Collections.singletonList("iface1"));
312         /////////
313         // SUT //
314         /////////
315         vpnsch.programScfToVpnPipeline(VPN_NAME, SCF_TAG, SERV_CHAIN_TAG, DPN_ID.longValue(), LPORT_TAG,
316                                        /* lastServiceChain */ false,
317                                        NwConstants.ADD_FLOW);
318         ////////////
319         // Verify //
320         ////////////
321
322         // Verifying installed flows
323         ArgumentCaptor<Flow> argumentCaptor = ArgumentCaptor.forClass(Flow.class);
324         verify(mdsalMgr, times(1)).installFlow((BigInteger)anyObject(), argumentCaptor.capture());
325         List<Flow> installedFlowsCaptured = argumentCaptor.getAllValues();
326         assert installedFlowsCaptured.size() == 1;
327         Flow expectedLportDispatcherFlowEntity =
328             VpnServiceChainUtils.buildLPortDispFromScfToL3VpnFlow(VPN_ID, DPN_ID, LPORT_TAG, NwConstants.ADD_FLOW);
329         assert new FlowMatcher(expectedLportDispatcherFlowEntity).matches(installedFlowsCaptured.get(0));
330
331         // Verifying VpnToDpn update
332         String vpnPseudoPortIfaceName =
333             VpnServiceChainUtils.buildVpnPseudoPortIfName(DPN_ID.longValue(), SCF_TAG, SERV_CHAIN_TAG, LPORT_TAG);
334         verify(vpnFootprintService).updateVpnToDpnMapping(eq(DPN_ID), eq(VPN_NAME), eq(RD), eq(vpnPseudoPortIfaceName),
335                                                           eq(null), eq(Boolean.TRUE));
336     }
337
338
339     @Test
340     public void testProgramVpnToScfWithVpnIfacesAlreadyBound() throws Exception {
341
342         /////////////////////
343         // Basic stubbing //
344         /////////////////////
345         String ifaceName = "eth0";
346         stubGetRouteDistinguisher(VPN_NAME, RD);
347         stubGetVpnInstance(RD, "1.2.3.4", ifaceName);
348         VrfEntry vrfEntry = FibHelper.getVrfEntryBuilder("11.12.13.14", 2000L, DC_GW_IP, RouteOrigin.STATIC, null)
349                 .build();
350         stubGetVrfEntries(RD, Collections.singletonList(vrfEntry));
351         stubReadVpnToDpnList(RD, DPN_ID, Collections.singletonList(ifaceName));
352         stubScfIsBoundOnIface(SCF_TAG, ifaceName);
353
354         /////////
355         // SUT //
356         /////////
357         short tableId = 10;
358         vpnsch.programVpnToScfPipeline(VPN_NAME, tableId, SCF_TAG, LPORT_TAG, NwConstants.ADD_FLOW);
359
360         ////////////
361         // Verify //
362         ////////////
363         ArgumentCaptor<FlowEntity> argumentCaptor = ArgumentCaptor.forClass(FlowEntity.class);
364         verify(mdsalMgr, times(2)).installFlow(argumentCaptor.capture());
365         List<FlowEntity> installedFlowsCaptured = argumentCaptor.getAllValues();
366         assert installedFlowsCaptured.size() == 2;
367
368         RoutePaths routePath = vrfEntry.getRoutePaths().get(0);
369         FlowEntity expectedLFibFlowEntity =
370             VpnServiceChainUtils.buildLFibVpnPseudoPortFlow(DPN_ID, routePath.getLabel(),
371                     routePath.getNexthopAddress(), LPORT_TAG);
372         assert new FlowEntityMatcher(expectedLFibFlowEntity).matches(installedFlowsCaptured.get(0));
373
374         FlowEntity expectedLPortDispatcher =
375             VpnServiceChainUtils.buildLportFlowDispForVpnToScf(DPN_ID, LPORT_TAG, SCF_TAG, tableId);
376         assert new FlowEntityMatcher(expectedLPortDispatcher).matches(installedFlowsCaptured.get(1));
377
378     }
379
380     @Test
381     public void testProgramVpnToScfWithIfacesNotBound() throws Exception {
382
383         /////////////////////
384         // Basic stubbing //
385         /////////////////////
386         String ifaceName = "eth0";
387         stubGetRouteDistinguisher(VPN_NAME, RD);
388         stubGetVpnInstance(RD, "1.2.3.4", ifaceName);
389         VrfEntry vrfEntry = FibHelper.getVrfEntryBuilder("11.12.13.14", 2000L, DC_GW_IP, RouteOrigin.STATIC, null)
390                 .build();
391         stubGetVrfEntries(RD, Collections.singletonList(vrfEntry));
392         stubReadVpnToDpnList(RD, DPN_ID, Collections.singletonList(ifaceName));
393         stubScfIsNotBoundOnIface(SCF_TAG, ifaceName);
394
395         /////////
396         // SUT //
397         /////////
398         short tableId = 10;
399         vpnsch.programVpnToScfPipeline(VPN_NAME, tableId, SCF_TAG, LPORT_TAG, NwConstants.ADD_FLOW);
400
401         ////////////
402         // Verify //
403         ////////////
404         ArgumentCaptor<FlowEntity> argumentCaptor = ArgumentCaptor.forClass(FlowEntity.class);
405         verify(ifaceMgr).bindService(eq(ifaceName), eq(ServiceModeIngress.class), anyObject());
406         verify(mdsalMgr, times(2)).installFlow(argumentCaptor.capture());
407         List<FlowEntity> installedFlowsCaptured = argumentCaptor.getAllValues();
408         assert installedFlowsCaptured.size() == 2;
409
410         RoutePaths routePath = vrfEntry.getRoutePaths().get(0);
411         FlowEntity expectedLFibFlowEntity =
412             VpnServiceChainUtils.buildLFibVpnPseudoPortFlow(DPN_ID, routePath.getLabel(),
413                     routePath.getNexthopAddress(), LPORT_TAG);
414         assert new FlowEntityMatcher(expectedLFibFlowEntity).matches(installedFlowsCaptured.get(0));
415
416         FlowEntity expectedLPortDispatcher =
417             VpnServiceChainUtils.buildLportFlowDispForVpnToScf(DPN_ID, LPORT_TAG, SCF_TAG, tableId);
418         assert new FlowEntityMatcher(expectedLPortDispatcher).matches(installedFlowsCaptured.get(1));
419
420     }
421
422     @Test
423     public void testBindScfOnVpnInterfaceWithScfAlreadyBound() throws Exception {
424         String ifName = "eth4";
425         int scfTag = 30;
426         stubScfIsBoundOnIface(scfTag, ifName);
427
428         vpnsch.bindScfOnVpnInterface(ifName, scfTag);
429
430         verify(ifaceMgr, never()).bindService(anyObject(), anyObject(), anyObject());
431     }
432
433     @Test
434     public void testBindScfOnVpnInterfaceWithScfNotBound() throws Exception {
435         String ifName = "eth4";
436         int scfTag = 30;
437         stubScfIsNotBoundOnIface(scfTag, ifName);
438
439         vpnsch.bindScfOnVpnInterface(ifName, scfTag);
440
441         verify(ifaceMgr).bindService(eq(ifName), eq(ServiceModeIngress.class), anyObject());
442     }
443 }