2 * Copyright (c) 2014 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.bgpcep.programming.impl;
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;
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;
53 public class ProgrammingServiceImplTest extends AbstractProgrammingTest {
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;
64 public void setUp() throws Exception {
66 this.mockedExecutorWrapper = new MockedExecutorWrapper();
67 this.mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
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();
77 public void tearDown() throws Exception {
78 this.singletonService.closeServiceInstance();
79 this.testedProgrammingService.close();
83 public void testScheduleInstruction() throws Exception {
84 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
85 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
87 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
89 // assert Schedule to executor
90 this.mockedExecutorWrapper.assertSubmittedTasksSize(1);
92 // assert Notification
93 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
94 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(),
95 InstructionStatus.Scheduled);
99 public void testScheduleDependingInstruction() throws Exception {
100 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
101 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
103 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
105 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
107 // First is in state scheduled, so second could not be scheduled yet
108 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
112 public void testScheduleDependingInstructionToFail() throws Exception {
114 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit",
116 } catch (final SchedulerException e) {
117 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
118 this.mockedNotificationServiceWrapper.assertNotificationsCount(0);
121 fail("Instruction schedule should fail on unresolved dependencies");
125 public void testCancelInstruction() throws Exception {
126 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
127 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
128 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
130 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
131 this.testedProgrammingService.cancelInstruction(mockedCancel);
133 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
134 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
136 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
137 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(),
138 InstructionStatus.Cancelled);
142 public void testCancelDependantInstruction() throws Exception {
143 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
144 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
145 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
147 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
148 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3",
149 "mockedSubmit1", "mockedSubmit2");
150 this.testedProgrammingService.scheduleInstruction(mockedSubmit3);
152 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
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);
165 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
166 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
167 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit3.getId()));
171 public void testCleanInstructions() throws Exception {
172 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
173 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
174 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
176 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
178 final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
179 final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
180 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
182 ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = this.testedProgrammingService
183 .cleanInstructions(cleanInstructionsInput);
185 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
187 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
189 cleanedInstructionOutput = this.testedProgrammingService.cleanInstructions(cleanInstructionsInput);
190 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
192 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
193 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
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());
203 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
205 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
209 public void testCloseProgrammingService() throws Exception {
210 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
211 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
212 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
214 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
216 this.testedProgrammingService.close();
218 this.mockedNotificationServiceWrapper
219 .assertNotificationsCount(1/* First scheduled */ + 2/* Both cancelled at close */);
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));
229 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
230 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
231 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
233 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
237 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
238 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
239 InstructionStatus.Cancelled);
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));
249 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
250 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
251 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
253 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
255 final Instruction i = future.get();
256 i.checkedExecutionStart();
257 i.executionCompleted(InstructionStatus.Successful, getDetails());
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
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));
274 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
275 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
276 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
278 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
280 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
282 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
284 final Instruction i = future.get();
285 i.checkedExecutionStart();
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);
296 // TODO test deadline with state Queued
299 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
300 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
301 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
303 final SubmitInstructionInput mockedSubmit2 =
304 getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
305 final ListenableFuture<Instruction> future2 = this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
307 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
309 Instruction instruction = future.get();
310 instruction.checkedExecutionStart();
311 instruction.executionCompleted(InstructionStatus.Successful, getDetails());
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);
321 instruction = future2.get();
322 instruction.checkedExecutionStart();
323 instruction.executionCompleted(InstructionStatus.Successful, getDetails());
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);
332 private static Details getDetails() {
333 return new DetailsBuilder().build();
336 private static SubmitInstructionInput getMockedSubmitInstructionInput(final String id,
337 final String... dependencyIds) {
338 return getMockedSubmitInstructionInput(id, Optional.empty(), dependencyIds);
341 private static SubmitInstructionInput getMockedSubmitInstructionInput(final String id,
342 final Optional<Nanotime> deadline, final String... dependencyIds) {
343 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
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));
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;
358 private static CancelInstructionInput getCancelInstruction(final String instructionId) {
359 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
360 builder.setId(new InstructionId(instructionId));
361 return builder.build();
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));