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