2 * Copyright (c) 2013 Cisco Systems, Inc. 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.openflowplugin.openflow.md.it;
10 import static org.ops4j.pax.exam.CoreOptions.options;
11 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Deque;
16 import java.util.List;
18 import java.util.concurrent.ArrayBlockingQueue;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.TimeoutException;
22 import javax.inject.Inject;
24 import org.junit.After;
25 import org.junit.Assert;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
31 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
32 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
33 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
34 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
37 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
38 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
39 import org.opendaylight.controller.test.sal.binding.it.TestHelper;
40 import org.opendaylight.openflowjava.protocol.impl.clients.ClientEvent;
41 import org.opendaylight.openflowjava.protocol.impl.clients.ScenarioHandler;
42 import org.opendaylight.openflowjava.protocol.impl.clients.SimpleClient;
43 import org.opendaylight.openflowjava.protocol.impl.clients.SleepEvent;
44 import org.opendaylight.openflowjava.protocol.impl.clients.WaitForMessageEvent;
45 import org.opendaylight.openflowjava.util.ByteBufUtils;
46 import org.opendaylight.openflowplugin.openflow.md.core.ThreadPoolLoggingExecutor;
47 import org.opendaylight.openflowplugin.openflow.md.core.sal.OpenflowPluginProvider;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DecNwTtlCaseBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.dec.nw.ttl._case.DecNwTtl;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.dec.nw.ttl._case.DecNwTtlBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
78 import org.opendaylight.yangtools.yang.binding.DataObject;
79 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
80 import org.ops4j.pax.exam.Configuration;
81 import org.ops4j.pax.exam.Option;
82 import org.ops4j.pax.exam.junit.PaxExam;
83 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
84 import org.ops4j.pax.exam.spi.reactors.PerClass;
85 import org.ops4j.pax.exam.util.Filter;
86 import org.osgi.framework.BundleContext;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
90 import com.google.common.base.Optional;
91 import com.google.common.util.concurrent.CheckedFuture;
92 import com.google.common.util.concurrent.FutureCallback;
93 import com.google.common.util.concurrent.Futures;
96 * covers basic handshake scenarios
98 @RunWith(PaxExam.class)
99 @ExamReactorStrategy(PerClass.class)
100 public class OFPluginFlowTest {
102 static final Logger LOG = LoggerFactory
103 .getLogger(OFPluginFlowTest.class);
105 private static final ArrayBlockingQueue<Runnable> SCENARIO_POOL_QUEUE = new ArrayBlockingQueue<>(1);
107 @Inject @Filter(timeout=60000)
108 OpenflowPluginProvider openflowPluginProvider;
110 @Inject @Filter(timeout=60000)
113 @Inject @Filter(timeout=60000)
114 static DataBroker dataBroker;
116 @Inject @Filter(timeout=60000)
117 NotificationProviderService notificationService;
119 private SimpleClient switchSim;
120 private ThreadPoolLoggingExecutor scenarioPool;
124 * @throws InterruptedException
127 public void setUp() throws InterruptedException {
128 LOG.debug("openflowPluginProvider: "+openflowPluginProvider);
129 scenarioPool = new ThreadPoolLoggingExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, SCENARIO_POOL_QUEUE, "scenario");
130 //FIXME: plugin should provide service exposing startup result via future
138 public void tearDown() {
140 LOG.debug("tearing down simulator");
141 switchSim.getScenarioDone().get(getFailSafeTimeout(), TimeUnit.MILLISECONDS);
142 } catch (Exception e) {
143 String msg = "waiting for scenario to finish failed: "+e.getMessage();
147 scenarioPool.shutdownNow();
148 SCENARIO_POOL_QUEUE.clear();
152 LOG.debug("checking if simulator succeeded to connect to controller");
153 boolean simulatorWasOnline = switchSim.getIsOnlineFuture().get(100, TimeUnit.MILLISECONDS);
154 Assert.assertTrue("simulator failed to connect to controller", simulatorWasOnline);
155 } catch (Exception e) {
156 String message = "simulator probably failed to connect to controller";
157 LOG.error(message, e);
158 Assert.fail(message);
162 final class TriggerTestListener implements DataChangeListener {
164 public TriggerTestListener() {
169 public void onDataChanged(
170 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> arg0) {
171 Set<InstanceIdentifier<?>> keySet = arg0.getCreatedData().keySet();
172 if (keySet.size() == 1) {
173 for (InstanceIdentifier<?> key : keySet) {
174 InstanceIdentifier<FlowCapableNode> neededKey =
175 key.firstIdentifierOf(FlowCapableNode.class);
176 if (neededKey != null) {
177 LOG.info("Node was added (brm) {}", neededKey);
178 writeFlow(createTestFlow(), neededKey);
187 * test basic integration with OFLib running the handshake
191 public void testFlowMod() throws Exception {
192 LOG.debug("testFlowMod integration test");
193 TriggerTestListener brmListener = new TriggerTestListener();
195 dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
196 getWildcardPath(), brmListener, DataChangeScope.BASE);
198 switchSim = createSimpleClient();
199 switchSim.setSecuredClient(false);
200 Deque<ClientEvent> handshakeScenario = ScenarioFactory.createHandshakeScenarioVBM(
201 ScenarioFactory.VERSION_BITMAP_13, (short) 0, ScenarioFactory.VERSION_BITMAP_10_13, false);
202 handshakeScenario.addFirst(new SleepEvent(6000L));
203 ScenarioFactory.appendPostHandshakeScenario(handshakeScenario, true);
204 WaitForMessageEvent flowModEvent = new WaitForMessageEvent(ByteBufUtils
206 "04 0e 00 58 00 00 00 05 00 00 00 00 00 00 00 0a "
207 + "00 00 00 00 00 00 00 0a 00 00 00 00 00 00 80 00 "
208 + "ff ff ff ff ff ff ff ff ff ff ff ff 00 01 00 00 "
209 + "00 01 00 16 80 00 0a 02 08 00 80 00 19 08 0a 00 "
210 + "00 01 ff ff ff 00 00 00 00 04 00 10 00 00 00 00 "
211 + "00 18 00 08 00 00 00 00"));
212 handshakeScenario.addFirst(flowModEvent);
213 ScenarioHandler scenario = new ScenarioHandler(handshakeScenario);
214 switchSim.setScenarioHandler(scenario);
215 scenarioPool.execute(switchSim);
216 LOG.info("finishing testFlowMod");
219 private static InstanceIdentifier<?> getWildcardPath() {
220 return InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class);
226 private static SimpleClient createSimpleClient() {
227 return new SimpleClient("localhost", 6653);
231 * @return timeout for case of failure
233 private static long getFailSafeTimeout() {
239 * @return bundle options
242 public Option[] config() {
243 LOG.info("configuring...");
245 systemProperty("osgi.console").value("2401"),
246 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
247 systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
249 OFPaxOptionsAssistant.osgiConsoleBundles(),
250 OFPaxOptionsAssistant.loggingBudles(),
252 TestHelper.junitAndMockitoBundles(),
253 TestHelper.mdSalCoreBundles(),
254 TestHelper.configMinumumBundles(),
255 TestHelper.baseModelBundles(),
257 OFPaxOptionsAssistant.ofPluginBundles());
260 static FlowBuilder createTestFlow() {
262 FlowBuilder flow = new FlowBuilder();
263 flow.setMatch(createMatch1().build());
264 flow.setInstructions(createDecNwTtlInstructions().build());
266 FlowId flowId = new FlowId("127");
267 FlowKey key = new FlowKey(flowId);
268 if (null == flow.isBarrier()) {
269 flow.setBarrier(Boolean.FALSE);
271 BigInteger value = BigInteger.TEN;
272 flow.setCookie(new FlowCookie(value));
273 flow.setCookieMask(new FlowCookie(value));
274 flow.setHardTimeout(0);
275 flow.setIdleTimeout(0);
276 flow.setInstallHw(false);
277 flow.setStrict(false);
278 flow.setContainerName(null);
279 flow.setFlags(new FlowModFlags(false, false, false, false, true));
281 flow.setTableId(tableId);
284 flow.setFlowName("Foo" + "X" + "f1");
289 private static MatchBuilder createMatch1() {
290 MatchBuilder match = new MatchBuilder();
291 Ipv4MatchBuilder ipv4Match = new Ipv4MatchBuilder();
292 Ipv4Prefix prefix = new Ipv4Prefix("10.0.0.1/24");
293 ipv4Match.setIpv4Destination(prefix);
294 Ipv4Match i4m = ipv4Match.build();
295 match.setLayer3Match(i4m);
297 EthernetMatchBuilder eth = new EthernetMatchBuilder();
298 EthernetTypeBuilder ethTypeBuilder = new EthernetTypeBuilder();
299 ethTypeBuilder.setType(new EtherType(0x0800L));
300 eth.setEthernetType(ethTypeBuilder.build());
301 match.setEthernetMatch(eth.build());
305 private static InstructionsBuilder createDecNwTtlInstructions() {
306 DecNwTtlBuilder ta = new DecNwTtlBuilder();
307 DecNwTtl decNwTtl = ta.build();
308 ActionBuilder ab = new ActionBuilder();
309 ab.setAction(new DecNwTtlCaseBuilder().setDecNwTtl(decNwTtl).build());
310 ab.setKey(new ActionKey(0));
311 // Add our drop action to a list
312 List<Action> actionList = new ArrayList<Action>();
313 actionList.add(ab.build());
315 // Create an Apply Action
316 ApplyActionsBuilder aab = new ApplyActionsBuilder();
317 aab.setAction(actionList);
319 // Wrap our Apply Action in an Instruction
320 InstructionBuilder ib = new InstructionBuilder();
321 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
322 ib.setKey(new InstructionKey(0));
325 // Put our Instruction in a list of Instructions
326 InstructionsBuilder isb = new InstructionsBuilder();
327 List<Instruction> instructions = new ArrayList<Instruction>();
328 instructions.add(ib.build());
329 ib.setKey(new InstructionKey(0));
330 isb.setInstruction(instructions);
334 static void writeFlow(FlowBuilder flow, InstanceIdentifier<FlowCapableNode> flowNodeIdent) {
335 ReadWriteTransaction modification = dataBroker.newReadWriteTransaction();
336 final InstanceIdentifier<Flow> path1 = flowNodeIdent.child(Table.class, new TableKey(flow.getTableId()))
337 .child(Flow.class, flow.getKey());
338 modification.merge(LogicalDatastoreType.CONFIGURATION, path1, flow.build(), true);
339 CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
340 Futures.addCallback(commitFuture, new FutureCallback<Void>() {
342 public void onSuccess(Void aVoid) {
343 LOG.debug("Write of flow on device succeeded.");
347 public void onFailure(Throwable throwable) {
348 LOG.error("Write of flow on device failed.", throwable);
353 //TODO move to separate test util class
354 private final static Flow readFlow(InstanceIdentifier<Flow> flow) {
355 Flow searchedFlow = null;
356 ReadTransaction rt = dataBroker.newReadOnlyTransaction();
357 CheckedFuture<Optional<Flow>, ReadFailedException> flowFuture =
358 rt.read(LogicalDatastoreType.CONFIGURATION, flow);
361 Optional<Flow> maybeFlow = flowFuture.checkedGet(500, TimeUnit.SECONDS);
362 if(maybeFlow.isPresent()) {
363 searchedFlow = maybeFlow.get();
365 } catch (TimeoutException e) {
366 LOG.error("Future timed out. Getting FLOW from DataStore failed.", e);
367 } catch (ReadFailedException e) {
368 LOG.error("Something wrong happened in DataStore. Getting FLOW for userId {} failed.", e);