Merge "BUG-2571 : added parser/serializer for flowspec NLRI"
[bgpcep.git] / programming / impl / src / test / java / org / opendaylight / bgpcep / programming / impl / ProgrammingServiceImplTest.java
1 /*
2  * Copyright (c) 2014 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.bgpcep.programming.impl;
9
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertThat;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.mock;
18 import com.google.common.base.Optional;
19 import com.google.common.collect.Lists;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import io.netty.util.HashedWheelTimer;
22 import io.netty.util.Timer;
23 import java.io.IOException;
24 import java.math.BigInteger;
25 import java.util.List;
26 import java.util.concurrent.ExecutionException;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.opendaylight.bgpcep.programming.NanotimeUtil;
31 import org.opendaylight.bgpcep.programming.spi.Instruction;
32 import org.opendaylight.bgpcep.programming.spi.SchedulerException;
33 import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
34 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelInstructionInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelInstructionInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsOutput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionStatus;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionsQueue;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.Nanotime;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.SubmitInstructionInput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.InstructionKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.Details;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.DetailsBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
52
53 public class ProgrammingServiceImplTest extends AbstractDataBrokerTest {
54
55     public static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
56
57     private MockedExecutorWrapper mockedExecutorWrapper;
58     private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
59     private ProgrammingServiceImpl testedProgrammingService;
60     private final Timer timer = new HashedWheelTimer();
61
62     @Before
63     public void setUp() throws IOException, YangSyntaxErrorException {
64         mockedExecutorWrapper = new MockedExecutorWrapper();
65         mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
66
67         testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(), mockedNotificationServiceWrapper.getMockedNotificationService(), mockedExecutorWrapper.getMockedExecutor(), timer);
68     }
69
70     @After
71     public void tearDown() throws Exception {
72     }
73
74     @Test
75     public void testScheduleInstruction() throws Exception {
76         final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
77         testedProgrammingService.scheduleInstruction(mockedSubmit);
78
79         assertTrue(assertInstructionExists(mockedSubmit.getId()));
80
81         // assert Schedule to executor
82         mockedExecutorWrapper.assertSubmittedTasksSize(1);
83
84         // assert Notification
85         mockedNotificationServiceWrapper.assertNotificationsCount(1);
86         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(), InstructionStatus.Scheduled);
87     }
88
89     @Test
90     public void testScheduleDependingInstruction() throws Exception {
91         testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
92         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
93         testedProgrammingService.scheduleInstruction(mockedSubmit2);
94
95         mockedExecutorWrapper.assertSubmittedTasksSize(2);
96
97         // First is in state scheduled, so second could not be scheduled yet
98         mockedNotificationServiceWrapper.assertNotificationsCount(1);
99     }
100
101     @Test
102     public void testScheduleDependingInstructionToFail() throws Exception {
103         try {
104             testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit", "dep1"));
105         } catch (final SchedulerException e) {
106             assertThat(e.getMessage(), containsString("Unknown dependency ID"));
107             mockedNotificationServiceWrapper.assertNotificationsCount(0);
108             return;
109         }
110         fail("Instruction schedule should fail on unresolved dependencies");
111     }
112
113     @Test
114     public void testCancelInstruction() throws Exception {
115         final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
116         testedProgrammingService.scheduleInstruction(mockedSubmit);
117
118         assertTrue(assertInstructionExists(mockedSubmit.getId()));
119
120         final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
121         testedProgrammingService.cancelInstruction(mockedCancel);
122
123         assertTrue(assertInstructionExists(mockedSubmit.getId()));
124
125         mockedExecutorWrapper.assertSubmittedTasksSize(2);
126
127         mockedNotificationServiceWrapper.assertNotificationsCount(2);
128         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(), InstructionStatus.Cancelled);
129     }
130
131     @Test
132     public void testCancelDependantInstruction() throws Exception {
133         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
134         testedProgrammingService.scheduleInstruction(mockedSubmit1);
135         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
136         testedProgrammingService.scheduleInstruction(mockedSubmit2);
137         final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3", "mockedSubmit1", "mockedSubmit2");
138         testedProgrammingService.scheduleInstruction(mockedSubmit3);
139
140         testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
141
142         mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/+ 3 /*First and all dependencies cancelled*/);
143         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(), InstructionStatus.Scheduled);
144         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
145         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(), InstructionStatus.Cancelled);
146         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(), InstructionStatus.Cancelled);
147
148         assertTrue(assertInstructionExists(mockedSubmit1.getId()));
149         assertTrue(assertInstructionExists(mockedSubmit2.getId()));
150         assertTrue(assertInstructionExists(mockedSubmit3.getId()));
151     }
152
153     @Test
154     public void testCleanInstructions() throws Exception {
155         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
156         testedProgrammingService.scheduleInstruction(mockedSubmit1);
157         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
158         testedProgrammingService.scheduleInstruction(mockedSubmit2);
159
160         final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
161         final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
162                 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
163
164         ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
165
166         assertCleanInstructionOutput(cleanedInstructionOutput, 2);
167
168         testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
169
170         cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
171         assertCleanInstructionOutput(cleanedInstructionOutput, 0);
172
173         assertFalse(assertInstructionExists(mockedSubmit1.getId()));
174         assertFalse(assertInstructionExists(mockedSubmit2.getId()));
175     }
176
177     private void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput,
178             final int unflushedCount) throws InterruptedException, java.util.concurrent.ExecutionException {
179         if (unflushedCount == 0) {
180             final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
181             assertTrue(unflushed == null || unflushed.isEmpty());
182         } else {
183             assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
184         }
185         assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
186     }
187
188     @Test
189     public void testCloseProgrammingService() throws Exception {
190         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
191         testedProgrammingService.scheduleInstruction(mockedSubmit1);
192         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
193         testedProgrammingService.scheduleInstruction(mockedSubmit2);
194
195         testedProgrammingService.close();
196
197         mockedNotificationServiceWrapper.assertNotificationsCount(1/* First scheduled */+ 2/* Both cancelled at close */);
198     }
199
200     @Test(timeout = 30 * 1000)
201     public void testTimeoutWhileScheduledTransaction() throws Exception {
202         final BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
203         final Nanotime current = NanotimeUtil.currentTime();
204         final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
205
206         final Optional<Nanotime> deadline = Optional.of(deadlineNano);
207         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
208         final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
209
210         mockedNotificationServiceWrapper.assertNotificationsCount(1);
211
212         future.get();
213
214         Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
215
216         mockedNotificationServiceWrapper.assertNotificationsCount(2);
217         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
218     }
219
220     @Test(timeout = 30 * 1000)
221     public void testTimeoutWhileSuccessfulTransaction() throws Exception {
222         final BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
223         final Nanotime current = NanotimeUtil.currentTime();
224         final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
225
226         final Optional<Nanotime> deadline = Optional.of(deadlineNano);
227         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
228         final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
229
230         mockedNotificationServiceWrapper.assertNotificationsCount(1);
231
232         final Instruction i = future.get();
233         i.checkedExecutionStart();
234         i.executionCompleted(InstructionStatus.Successful, getDetails());
235
236         Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
237
238         mockedNotificationServiceWrapper.assertNotificationsCount(3);
239         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
240         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
241         // Timeout in success should not do anything
242     }
243
244     @Test(timeout = 30 * 1000)
245     public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
246         final BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
247         final Nanotime current = NanotimeUtil.currentTime();
248         final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
249
250         final Optional<Nanotime> deadline = Optional.of(deadlineNano);
251         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
252         final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
253
254         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
255         testedProgrammingService.scheduleInstruction(mockedSubmit2);
256
257         mockedNotificationServiceWrapper.assertNotificationsCount(1);
258
259         final Instruction i = future.get();
260         i.checkedExecutionStart();
261
262         Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
263
264         mockedNotificationServiceWrapper.assertNotificationsCount(4);
265         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
266         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Unknown);
267         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Cancelled);
268     }
269
270     // TODO test deadline with state Queued
271
272     @Test
273     public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
274         final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
275         final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
276
277         final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
278         final ListenableFuture<Instruction> future2 = testedProgrammingService.scheduleInstruction(mockedSubmit2);
279
280         mockedNotificationServiceWrapper.assertNotificationsCount(1);
281
282         Instruction i = future.get();
283         i.checkedExecutionStart();
284         i.executionCompleted(InstructionStatus.Successful, getDetails());
285
286         mockedNotificationServiceWrapper.assertNotificationsCount(4);
287         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
288         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
289         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Scheduled);
290
291         i = future2.get();
292         i.checkedExecutionStart();
293         i.executionCompleted(InstructionStatus.Successful, getDetails());
294
295         mockedNotificationServiceWrapper.assertNotificationsCount(6);
296         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(), InstructionStatus.Executing);
297         mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(), InstructionStatus.Successful);
298     }
299
300     private Details getDetails() {
301         return new DetailsBuilder().build();
302     }
303
304     private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final String... dependencyIds) {
305         return getMockedSubmitInstructionInput(id, Optional.<Nanotime> absent(), dependencyIds);
306     }
307
308     private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final Optional<Nanotime> deadline, final String... dependencyIds) {
309         final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
310
311         doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
312         final List<InstructionId> dependencies = Lists.newArrayList();
313         for (String dependencyId : dependencyIds) {
314             dependencies.add(getInstructionId(dependencyId));
315         }
316
317         doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
318         doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
319         doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))).when(mockedSubmitInstruction).getDeadline();
320         return mockedSubmitInstruction;
321     }
322
323     private CancelInstructionInput getCancelInstruction(final String instructionId) {
324         final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
325         builder.setId(getInstructionId(instructionId));
326         return builder.build();
327     }
328
329     private InstructionId getInstructionId(final String id) {
330         return new InstructionId(id);
331     }
332
333     private boolean assertInstructionExists(final InstructionId id) {
334         try {
335             return getDataBroker().newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(InstructionsQueue.class).build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.Instruction.class,
336                     new InstructionKey(id))).get().isPresent();
337         } catch (InterruptedException | ExecutionException e) {
338             return false;
339         }
340     }
341 }