BUG-837: it unstable (added missing bundles)
[openflowplugin.git] / openflowplugin-it / src / test / java / org / opendaylight / openflowplugin / openflow / md / it / OFPluginFlowTest.java
1 /**
2  * Copyright (c) 2013 Cisco Systems, Inc. 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.openflowplugin.openflow.md.it;
9
10 import static org.ops4j.pax.exam.CoreOptions.options;
11 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
12
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Deque;
16 import java.util.List;
17 import java.util.Set;
18 import java.util.concurrent.ArrayBlockingQueue;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.TimeoutException;
21
22 import javax.inject.Inject;
23
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;
89
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;
94
95 /**
96  * covers basic handshake scenarios
97  */
98 @RunWith(PaxExam.class)
99 @ExamReactorStrategy(PerClass.class)
100 public class OFPluginFlowTest {
101
102     static final Logger LOG = LoggerFactory
103             .getLogger(OFPluginFlowTest.class);
104
105     private static final ArrayBlockingQueue<Runnable> SCENARIO_POOL_QUEUE = new ArrayBlockingQueue<>(1);
106
107     @Inject @Filter(timeout=60000)
108     OpenflowPluginProvider openflowPluginProvider;
109
110     @Inject @Filter(timeout=60000)
111     BundleContext ctx;
112
113     @Inject @Filter(timeout=60000)
114     static DataBroker dataBroker;
115
116     @Inject @Filter(timeout=60000)
117     NotificationProviderService notificationService;
118
119     private SimpleClient switchSim;
120     private ThreadPoolLoggingExecutor scenarioPool;
121
122     /**
123      * test setup
124      * @throws InterruptedException
125      */
126     @Before
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
131         Thread.sleep(5000);
132     }
133
134     /**
135      * test tear down
136      */
137     @After
138     public void tearDown() {
139         try {
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();
144             LOG.error(msg, e);
145             Assert.fail(msg);
146         } finally {
147             scenarioPool.shutdownNow();
148             SCENARIO_POOL_QUEUE.clear();
149         }
150
151         try {
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);
159         }
160     }
161
162     final class TriggerTestListener implements DataChangeListener {
163
164         public TriggerTestListener() {
165             // NOOP
166         }
167
168         @Override
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);
179                         break;
180                     }
181                 }
182             }
183         }
184     }
185
186     /**
187      * test basic integration with OFLib running the handshake
188      * @throws Exception
189      */
190     @Test
191     public void testFlowMod() throws Exception {
192         LOG.debug("testFlowMod integration test");
193         TriggerTestListener brmListener = new TriggerTestListener();
194
195         dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
196                 getWildcardPath(), brmListener, DataChangeScope.BASE);
197
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
205                 .hexStringToBytes(
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");
217     }
218
219     private static InstanceIdentifier<?> getWildcardPath() {
220         return InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class);
221     }
222
223     /**
224      * @return
225      */
226     private static SimpleClient createSimpleClient() {
227         return new SimpleClient("localhost", 6653);
228     }
229
230     /**
231      * @return timeout for case of failure
232      */
233     private static long getFailSafeTimeout() {
234         return 20000;
235     }
236
237
238     /**
239      * @return bundle options
240      */
241     @Configuration
242     public Option[] config() {
243         LOG.info("configuring...");
244         return options(
245                 systemProperty("osgi.console").value("2401"),
246                 systemProperty("osgi.bundles.defaultStartLevel").value("4"),
247                 systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
248
249                 OFPaxOptionsAssistant.osgiConsoleBundles(),
250                 OFPaxOptionsAssistant.loggingBudles(),
251                 OFPaxOptionsAssistant.ofPluginBundles());
252     }
253
254     static FlowBuilder createTestFlow() {
255         short tableId = 0;
256         FlowBuilder flow = new FlowBuilder();
257         flow.setMatch(createMatch1().build());
258         flow.setInstructions(createDecNwTtlInstructions().build());
259
260         FlowId flowId = new FlowId("127");
261         FlowKey key = new FlowKey(flowId);
262         if (null == flow.isBarrier()) {
263             flow.setBarrier(Boolean.FALSE);
264         }
265         BigInteger value = BigInteger.TEN;
266         flow.setCookie(new FlowCookie(value));
267         flow.setCookieMask(new FlowCookie(value));
268         flow.setHardTimeout(0);
269         flow.setIdleTimeout(0);
270         flow.setInstallHw(false);
271         flow.setStrict(false);
272         flow.setContainerName(null);
273         flow.setFlags(new FlowModFlags(false, false, false, false, true));
274         flow.setId(flowId);
275         flow.setTableId(tableId);
276
277         flow.setKey(key);
278         flow.setFlowName("Foo" + "X" + "f1");
279
280         return flow;
281     }
282
283     private static MatchBuilder createMatch1() {
284         MatchBuilder match = new MatchBuilder();
285         Ipv4MatchBuilder ipv4Match = new Ipv4MatchBuilder();
286         Ipv4Prefix prefix = new Ipv4Prefix("10.0.0.1/24");
287         ipv4Match.setIpv4Destination(prefix);
288         Ipv4Match i4m = ipv4Match.build();
289         match.setLayer3Match(i4m);
290
291         EthernetMatchBuilder eth = new EthernetMatchBuilder();
292         EthernetTypeBuilder ethTypeBuilder = new EthernetTypeBuilder();
293         ethTypeBuilder.setType(new EtherType(0x0800L));
294         eth.setEthernetType(ethTypeBuilder.build());
295         match.setEthernetMatch(eth.build());
296         return match;
297     }
298
299     private static InstructionsBuilder createDecNwTtlInstructions() {
300         DecNwTtlBuilder ta = new DecNwTtlBuilder();
301         DecNwTtl decNwTtl = ta.build();
302         ActionBuilder ab = new ActionBuilder();
303         ab.setAction(new DecNwTtlCaseBuilder().setDecNwTtl(decNwTtl).build());
304         ab.setKey(new ActionKey(0));
305         // Add our drop action to a list
306         List<Action> actionList = new ArrayList<Action>();
307         actionList.add(ab.build());
308
309         // Create an Apply Action
310         ApplyActionsBuilder aab = new ApplyActionsBuilder();
311         aab.setAction(actionList);
312
313         // Wrap our Apply Action in an Instruction
314         InstructionBuilder ib = new InstructionBuilder();
315         ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
316         ib.setKey(new InstructionKey(0));
317         ib.setOrder(0);
318
319         // Put our Instruction in a list of Instructions
320         InstructionsBuilder isb = new InstructionsBuilder();
321         List<Instruction> instructions = new ArrayList<Instruction>();
322         instructions.add(ib.build());
323         ib.setKey(new InstructionKey(0));
324         isb.setInstruction(instructions);
325         return isb;
326     }
327
328     static void writeFlow(FlowBuilder flow, InstanceIdentifier<FlowCapableNode> flowNodeIdent) {
329         ReadWriteTransaction modification = dataBroker.newReadWriteTransaction();
330         final InstanceIdentifier<Flow> path1 = flowNodeIdent.child(Table.class, new TableKey(flow.getTableId()))
331                 .child(Flow.class, flow.getKey());
332         modification.merge(LogicalDatastoreType.CONFIGURATION, path1, flow.build(), true);
333         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
334         Futures.addCallback(commitFuture, new FutureCallback<Void>() {
335             @Override
336             public void onSuccess(Void aVoid) {
337                 LOG.debug("Write of flow on device succeeded.");
338             }
339
340             @Override
341             public void onFailure(Throwable throwable) {
342                 LOG.error("Write of flow on device failed.", throwable);
343             }
344         });
345     }
346
347     //TODO move to separate test util class
348     private final static Flow readFlow(InstanceIdentifier<Flow> flow) {
349         Flow searchedFlow = null;
350         ReadTransaction rt = dataBroker.newReadOnlyTransaction();
351         CheckedFuture<Optional<Flow>, ReadFailedException> flowFuture =
352             rt.read(LogicalDatastoreType.CONFIGURATION, flow);
353
354         try {
355           Optional<Flow> maybeFlow = flowFuture.checkedGet(500, TimeUnit.SECONDS);
356           if(maybeFlow.isPresent()) {
357               searchedFlow = maybeFlow.get();
358           }
359         } catch (TimeoutException e) {
360           LOG.error("Future timed out. Getting FLOW from DataStore failed.", e);
361         } catch (ReadFailedException e) {
362           LOG.error("Something wrong happened in DataStore. Getting FLOW for userId {} failed.", e);
363         }
364
365         return searchedFlow;
366     }
367 }