2 * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.cloudservicechain;
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;
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;
85 @RunWith(MockitoJUnitRunner.class)
86 public class VPNServiceChainHandlerTest {
88 static final Logger LOG = LoggerFactory.getLogger(VPNServiceChainHandler.class);
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";
99 private static MetricProvider metricProvider = new TestMetricProviderImpl();
101 private static JobCoordinatorImpl jobCoordinator = new JobCoordinatorImpl(metricProvider);
103 private VPNServiceChainHandler vpnsch; // SUT
105 @Mock DataBroker broker;
106 @Mock ReadOnlyTransaction readTx;
107 @Mock WriteTransaction writeTx;
108 @Mock IMdsalApiManager mdsalMgr;
109 @Mock IVpnFootprintService vpnFootprintService;
110 @Mock IInterfaceManager ifaceMgr;
113 public static void setUpBeforeClass() throws Exception {
117 public static void tearDownAfterClass() throws Exception {
118 jobCoordinator.destroy();
122 public void setUp() throws Exception {
124 when(broker.newReadOnlyTransaction()).thenReturn(readTx);
125 when(broker.newWriteOnlyTransaction()).thenReturn(writeTx);
126 CheckedFuture chkdFuture = mock(CheckedFuture.class);
127 when(writeTx.submit()).thenReturn(chkdFuture);
130 vpnsch = new VPNServiceChainHandler(broker, mdsalMgr, vpnFootprintService, ifaceMgr, jobCoordinator);
134 public void tearDown() throws Exception {
137 private <T extends DataObject> Matcher<InstanceIdentifier<T>> isIIdType(final Class<T> klass) {
138 return new TypeSafeMatcher<InstanceIdentifier<T>>() {
140 public void describeTo(Description desc) {
141 desc.appendText("Instance Identifier should have Target Type " + klass);
145 protected boolean matchesSafely(InstanceIdentifier<T> id) {
146 return id.getTargetType().equals(klass);
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();
155 InstanceIdentifier<VpnInstance> id = VpnServiceChainUtils.getVpnInstanceToVpnIdIdentifier(vpnName);
156 CheckedFuture chkdFuture = mock(CheckedFuture.class);
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);
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);
173 private void stubNoVpnInstanceForRD(String rd) throws Exception {
174 CheckedFuture<Optional<VpnInstanceOpDataEntry>, ReadFailedException> chkdFuture = mock(CheckedFuture.class);
175 when(chkdFuture.checkedGet()).thenReturn(Optional.absent());
177 InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class)
178 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
180 when(readTx.read(eq(LogicalDatastoreType.OPERATIONAL), eq(id))).thenReturn(chkdFuture);
183 private void stubGetVpnInstance(String rd, String ipAddress, String ifaceName) throws Exception {
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))
192 .setIpAddresses(Collections.singletonList(ipAddr))
193 .setVpnInterfaces(ifacesList);
195 VpnInstanceOpDataEntry vpnInstanceOpDataEntry =
196 new VpnInstanceOpDataEntryBuilder().setKey(new VpnInstanceOpDataEntryKey(rd))
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);
206 private void stubGetVrfEntries(String rd, List<VrfEntry> vrfEntryList)
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);
218 private void stubReadVpnToDpnList(String rd, BigInteger dpnId, List<String> vpnIfacesOnDpn)
221 List<VpnInterfaces> vpnIfacesList =
222 vpnIfacesOnDpn.stream()
223 .map((ifaceName) -> new VpnInterfacesBuilder().setKey(new VpnInterfacesKey(ifaceName))
224 .setInterfaceName(ifaceName).build())
225 .collect(Collectors.toList());
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);
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*/);
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);
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);
257 public void testprogramScfToVpnPipelineNullRd() throws Exception {
258 /////////////////////
260 /////////////////////
261 stubNoRdForVpnName(VPN_NAME);
262 /////////////////////
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
270 ArgumentCaptor<FlowEntity> argumentCaptor = ArgumentCaptor.forClass(FlowEntity.class);
271 verify(mdsalMgr, times(0)).installFlow(argumentCaptor.capture());
273 List<FlowEntity> installedFlowsCaptured = argumentCaptor.getAllValues();
274 assert installedFlowsCaptured.isEmpty();
279 public void testprogramScfToVpnPipelineNullVpnInstance() throws Exception {
281 /////////////////////
283 /////////////////////
284 stubGetRouteDistinguisher(VPN_NAME, RD);
285 stubNoVpnInstanceForRD(RD);
286 /////////////////////
288 /////////////////////
289 vpnsch.programScfToVpnPipeline(VPN_NAME, SCF_TAG, SERV_CHAIN_TAG, DPN_ID.longValue(), LPORT_TAG,
290 /* lastServiceChain */ false, NwConstants.ADD_FLOW);
292 ArgumentCaptor<FlowEntity> argumentCaptor = ArgumentCaptor.forClass(FlowEntity.class);
293 verify(mdsalMgr, times(0)).installFlow(argumentCaptor.capture());
295 List<FlowEntity> installedFlowsCaptured = argumentCaptor.getAllValues();
296 assert installedFlowsCaptured.isEmpty();
301 public void testprogramScfToVpnPipeline() throws Exception {
303 /////////////////////
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)
310 stubGetVrfEntries(RD, Collections.singletonList(vrfEntry));
311 stubReadVpnToDpnList(RD, DPN_ID, Collections.singletonList("iface1"));
315 vpnsch.programScfToVpnPipeline(VPN_NAME, SCF_TAG, SERV_CHAIN_TAG, DPN_ID.longValue(), LPORT_TAG,
316 /* lastServiceChain */ false,
317 NwConstants.ADD_FLOW);
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));
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));
340 public void testProgramVpnToScfWithVpnIfacesAlreadyBound() throws Exception {
342 /////////////////////
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)
350 stubGetVrfEntries(RD, Collections.singletonList(vrfEntry));
351 stubReadVpnToDpnList(RD, DPN_ID, Collections.singletonList(ifaceName));
352 stubScfIsBoundOnIface(SCF_TAG, ifaceName);
358 vpnsch.programVpnToScfPipeline(VPN_NAME, tableId, SCF_TAG, LPORT_TAG, NwConstants.ADD_FLOW);
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;
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));
374 FlowEntity expectedLPortDispatcher =
375 VpnServiceChainUtils.buildLportFlowDispForVpnToScf(DPN_ID, LPORT_TAG, SCF_TAG, tableId);
376 assert new FlowEntityMatcher(expectedLPortDispatcher).matches(installedFlowsCaptured.get(1));
381 public void testProgramVpnToScfWithIfacesNotBound() throws Exception {
383 /////////////////////
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)
391 stubGetVrfEntries(RD, Collections.singletonList(vrfEntry));
392 stubReadVpnToDpnList(RD, DPN_ID, Collections.singletonList(ifaceName));
393 stubScfIsNotBoundOnIface(SCF_TAG, ifaceName);
399 vpnsch.programVpnToScfPipeline(VPN_NAME, tableId, SCF_TAG, LPORT_TAG, NwConstants.ADD_FLOW);
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;
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));
416 FlowEntity expectedLPortDispatcher =
417 VpnServiceChainUtils.buildLportFlowDispForVpnToScf(DPN_ID, LPORT_TAG, SCF_TAG, tableId);
418 assert new FlowEntityMatcher(expectedLPortDispatcher).matches(installedFlowsCaptured.get(1));
423 public void testBindScfOnVpnInterfaceWithScfAlreadyBound() throws Exception {
424 String ifName = "eth4";
426 stubScfIsBoundOnIface(scfTag, ifName);
428 vpnsch.bindScfOnVpnInterface(ifName, scfTag);
430 verify(ifaceMgr, never()).bindService(anyObject(), anyObject(), anyObject());
434 public void testBindScfOnVpnInterfaceWithScfNotBound() throws Exception {
435 String ifName = "eth4";
437 stubScfIsNotBoundOnIface(scfTag, ifName);
439 vpnsch.bindScfOnVpnInterface(ifName, scfTag);
441 verify(ifaceMgr).bindService(eq(ifName), eq(ServiceModeIngress.class), anyObject());