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.CheckUtil.checkNotPresentOperational;
18 import static org.opendaylight.protocol.util.CheckUtil.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.List;
26 import java.util.Optional;
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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInputBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueue;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueueKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.Nanotime;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.SubmitInstructionInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.InstructionKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
52 public class ProgrammingServiceImplTest extends AbstractProgrammingTest {
54 private static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
55 private static final String INSTRUCTIONS_QUEUE_KEY = "test-instraction-queue";
56 private final Timer timer = new HashedWheelTimer();
57 private MockedExecutorWrapper mockedExecutorWrapper;
58 private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
59 private ProgrammingServiceImpl testedProgrammingService;
63 public void setUp() throws Exception {
65 this.mockedExecutorWrapper = new MockedExecutorWrapper();
66 this.mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
68 this.testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(),
69 this.mockedNotificationServiceWrapper.getMockedNotificationService(),
70 this.mockedExecutorWrapper.getMockedExecutor(), this.rpcRegistry, this.cssp, this.timer,
71 INSTRUCTIONS_QUEUE_KEY);
72 this.singletonService.instantiateServiceInstance();
76 public void tearDown() throws Exception {
77 this.singletonService.closeServiceInstance();
78 this.testedProgrammingService.close();
82 public void testScheduleInstruction() throws Exception {
83 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
84 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
86 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
88 // assert Schedule to executor
89 this.mockedExecutorWrapper.assertSubmittedTasksSize(1);
91 // assert Notification
92 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
93 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(),
94 InstructionStatus.Scheduled);
98 public void testScheduleDependingInstruction() throws Exception {
99 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
100 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
102 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
104 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
106 // First is in state scheduled, so second could not be scheduled yet
107 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
111 public void testScheduleDependingInstructionToFail() throws Exception {
113 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit",
115 } catch (final SchedulerException e) {
116 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
117 this.mockedNotificationServiceWrapper.assertNotificationsCount(0);
120 fail("Instruction schedule should fail on unresolved dependencies");
124 public void testCancelInstruction() throws Exception {
125 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
126 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
127 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
129 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
130 this.testedProgrammingService.cancelInstruction(mockedCancel);
132 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
133 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
135 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
136 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(),
137 InstructionStatus.Cancelled);
141 public void testCancelDependantInstruction() throws Exception {
142 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
143 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
144 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
146 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
147 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3",
148 "mockedSubmit1", "mockedSubmit2");
149 this.testedProgrammingService.scheduleInstruction(mockedSubmit3);
151 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
153 this.mockedNotificationServiceWrapper
154 .assertNotificationsCount(1 /*First Scheduled*/ + 3 /*First and all dependencies cancelled*/);
155 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(),
156 InstructionStatus.Scheduled);
157 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
158 InstructionStatus.Cancelled);
159 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(),
160 InstructionStatus.Cancelled);
161 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(),
162 InstructionStatus.Cancelled);
164 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
165 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
166 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit3.getId()));
170 public void testCleanInstructions() throws Exception {
171 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
172 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
173 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
175 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
177 final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
178 final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
179 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
181 ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = this.testedProgrammingService
182 .cleanInstructions(cleanInstructionsInput);
184 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
186 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
188 cleanedInstructionOutput = this.testedProgrammingService.cleanInstructions(cleanInstructionsInput);
189 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
191 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
192 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
195 private static void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>>
196 cleanedInstructionOutput, final int unflushedCount) throws InterruptedException,
197 java.util.concurrent.ExecutionException {
198 if (unflushedCount == 0) {
199 final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
200 assertTrue(unflushed == null || unflushed.isEmpty());
202 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
204 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
208 public void testCloseProgrammingService() throws Exception {
209 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
210 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
211 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
213 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
215 this.testedProgrammingService.close();
217 this.mockedNotificationServiceWrapper
218 .assertNotificationsCount(1/* First scheduled */ + 2/* Both cancelled at close */);
221 @Test(timeout = 30 * 1000)
222 public void testTimeoutWhileScheduledTransaction() throws Exception {
223 final BigInteger deadlineOffset = BigInteger.valueOf(
224 1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
225 final Nanotime current = NanotimeUtil.currentTime();
226 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
228 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
229 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
230 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
232 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
236 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
237 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
238 InstructionStatus.Cancelled);
241 @Test(timeout = 30 * 1000)
242 public void testTimeoutWhileSuccessfulTransaction() throws Exception {
243 final BigInteger deadlineOffset = BigInteger.valueOf(
244 1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
245 final Nanotime current = NanotimeUtil.currentTime();
246 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
248 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
249 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
250 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
252 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
254 final Instruction i = future.get();
255 i.checkedExecutionStart();
256 i.executionCompleted(InstructionStatus.Successful, getDetails());
258 this.mockedNotificationServiceWrapper.assertNotificationsCount(3);
259 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
260 InstructionStatus.Executing);
261 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
262 InstructionStatus.Successful);
263 // Timeout in success should not do anything
266 @Test(timeout = 30 * 1000)
267 public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
268 final BigInteger deadlineOffset = BigInteger.valueOf(
269 1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
270 final Nanotime current = NanotimeUtil.currentTime();
271 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
273 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
274 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
275 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
277 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
279 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
281 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
283 final Instruction i = future.get();
284 i.checkedExecutionStart();
286 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
287 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
288 InstructionStatus.Executing);
289 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
290 InstructionStatus.Unknown);
291 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
292 InstructionStatus.Cancelled);
295 // TODO test deadline with state Queued
298 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
299 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
300 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
302 final SubmitInstructionInput mockedSubmit2 =
303 getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
304 final ListenableFuture<Instruction> future2 = this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
306 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
308 Instruction instruction = future.get();
309 instruction.checkedExecutionStart();
310 instruction.executionCompleted(InstructionStatus.Successful, getDetails());
312 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
313 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
314 InstructionStatus.Executing);
315 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
316 InstructionStatus.Successful);
317 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
318 InstructionStatus.Scheduled);
320 instruction = future2.get();
321 instruction.checkedExecutionStart();
322 instruction.executionCompleted(InstructionStatus.Successful, getDetails());
324 this.mockedNotificationServiceWrapper.assertNotificationsCount(6);
325 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(),
326 InstructionStatus.Executing);
327 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(),
328 InstructionStatus.Successful);
331 private static Details getDetails() {
332 return new DetailsBuilder().build();
335 private static SubmitInstructionInput getMockedSubmitInstructionInput(final String id,
336 final String... dependencyIds) {
337 return getMockedSubmitInstructionInput(id, Optional.empty(), dependencyIds);
340 private static SubmitInstructionInput getMockedSubmitInstructionInput(final String id,
341 final Optional<Nanotime> deadline, final String... dependencyIds) {
342 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
344 doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
345 final List<InstructionId> dependencies = Lists.newArrayList();
346 for (final String dependencyId : dependencyIds) {
347 dependencies.add(new InstructionId(dependencyId));
350 doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
351 doReturn(new InstructionId(id)).when(mockedSubmitInstruction).getId();
352 doReturn(deadline.orElseGet(() -> new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))))
353 .when(mockedSubmitInstruction).getDeadline();
354 return mockedSubmitInstruction;
357 private static CancelInstructionInput getCancelInstruction(final String instructionId) {
358 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
359 builder.setId(new InstructionId(instructionId));
360 return builder.build();
363 private static KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming
364 .rev150720.instruction.queue.Instruction, InstructionKey> buildInstructionIID(final InstructionId id) {
365 return InstanceIdentifier.builder(InstructionsQueue.class, new InstructionsQueueKey(INSTRUCTIONS_QUEUE_KEY))
366 .build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720
367 .instruction.queue.Instruction.class, new InstructionKey(id));