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