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