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.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.doNothing;
17 import static org.mockito.Mockito.doReturn;
18 import static org.mockito.Mockito.mock;
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 java.util.concurrent.ExecutionException;
28 import org.junit.After;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.mockito.Mock;
32 import org.mockito.Mockito;
33 import org.mockito.MockitoAnnotations;
34 import org.opendaylight.bgpcep.programming.NanotimeUtil;
35 import org.opendaylight.bgpcep.programming.spi.Instruction;
36 import org.opendaylight.bgpcep.programming.spi.SchedulerException;
37 import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
38 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
39 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
40 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsOutput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueue;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueueKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.Nanotime;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.ProgrammingService;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.SubmitInstructionInput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.InstructionKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.common.RpcResult;
60 public class ProgrammingServiceImplTest extends AbstractDataBrokerTest {
62 private static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
63 private static final InstructionsQueueKey INSTRUCTIONS_QUEUE_KEY = new InstructionsQueueKey("test-instraction-queue");
64 private final Timer timer = new HashedWheelTimer();
65 private MockedExecutorWrapper mockedExecutorWrapper;
66 private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
67 private ProgrammingServiceImpl testedProgrammingService;
69 private RpcProviderRegistry rpcRegistry;
71 private RoutedRpcRegistration<ProgrammingService> registration;
74 public void setUp() throws Exception {
75 MockitoAnnotations.initMocks(this);
76 doReturn(this.registration).when(this.rpcRegistry).addRpcImplementation(Mockito.any(),
77 Mockito.any(ProgrammingService.class));
78 doNothing().when(this.registration).close();
79 this.mockedExecutorWrapper = new MockedExecutorWrapper();
80 this.mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
82 this.testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(),
83 this.mockedNotificationServiceWrapper.getMockedNotificationService(),
84 this.mockedExecutorWrapper.getMockedExecutor(), this.rpcRegistry, this.timer, INSTRUCTIONS_QUEUE_KEY);
88 public void tearDown() throws Exception {
89 this.testedProgrammingService.close();
93 public void testScheduleInstruction() throws Exception {
94 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
95 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
97 assertTrue(assertInstructionExists(mockedSubmit.getId()));
99 // assert Schedule to executor
100 this.mockedExecutorWrapper.assertSubmittedTasksSize(1);
102 // assert Notification
103 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
104 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(), InstructionStatus.Scheduled);
108 public void testScheduleDependingInstruction() throws Exception {
109 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
110 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
111 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
113 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
115 // First is in state scheduled, so second could not be scheduled yet
116 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
120 public void testScheduleDependingInstructionToFail() throws Exception {
122 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit", "dep1"));
123 } catch (final SchedulerException e) {
124 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
125 this.mockedNotificationServiceWrapper.assertNotificationsCount(0);
128 fail("Instruction schedule should fail on unresolved dependencies");
132 public void testCancelInstruction() throws Exception {
133 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
134 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
136 assertTrue(assertInstructionExists(mockedSubmit.getId()));
138 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
139 this.testedProgrammingService.cancelInstruction(mockedCancel);
141 assertTrue(assertInstructionExists(mockedSubmit.getId()));
143 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
145 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
146 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(), InstructionStatus.Cancelled);
150 public void testCancelDependantInstruction() throws Exception {
151 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
152 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
153 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
154 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
155 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3", "mockedSubmit1", "mockedSubmit2");
156 this.testedProgrammingService.scheduleInstruction(mockedSubmit3);
158 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
160 this.mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/+ 3 /*First and all dependencies cancelled*/);
161 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(), InstructionStatus.Scheduled);
162 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
163 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(), InstructionStatus.Cancelled);
164 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(), InstructionStatus.Cancelled);
166 assertTrue(assertInstructionExists(mockedSubmit1.getId()));
167 assertTrue(assertInstructionExists(mockedSubmit2.getId()));
168 assertTrue(assertInstructionExists(mockedSubmit3.getId()));
172 public void testCleanInstructions() throws Exception {
173 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
174 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
175 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
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.cleanInstructions(cleanInstructionsInput);
184 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
186 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
188 cleanedInstructionOutput = this.testedProgrammingService.cleanInstructions(cleanInstructionsInput);
189 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
191 assertFalse(assertInstructionExists(mockedSubmit1.getId()));
192 assertFalse(assertInstructionExists(mockedSubmit2.getId()));
195 private void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput,
196 final int unflushedCount) throws InterruptedException, java.util.concurrent.ExecutionException {
197 if (unflushedCount == 0) {
198 final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
199 assertTrue(unflushed == null || unflushed.isEmpty());
201 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
203 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
207 public void testCloseProgrammingService() throws Exception {
208 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
209 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
210 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
211 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
213 this.testedProgrammingService.close();
215 this.mockedNotificationServiceWrapper.assertNotificationsCount(1/* First scheduled */+ 2/* Both cancelled at close */);
218 @Test(timeout = 30 * 1000)
219 public void testTimeoutWhileScheduledTransaction() throws Exception {
220 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
221 final Nanotime current = NanotimeUtil.currentTime();
222 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
224 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
225 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
226 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
228 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
232 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
234 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
235 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
238 @Test(timeout = 30 * 1000)
239 public void testTimeoutWhileSuccessfulTransaction() throws Exception {
240 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
241 final Nanotime current = NanotimeUtil.currentTime();
242 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
244 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
245 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
246 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
248 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
250 final Instruction i = future.get();
251 i.checkedExecutionStart();
252 i.executionCompleted(InstructionStatus.Successful, getDetails());
254 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
256 this.mockedNotificationServiceWrapper.assertNotificationsCount(3);
257 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
258 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
259 // Timeout in success should not do anything
262 @Test(timeout = 30 * 1000)
263 public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
264 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
265 final Nanotime current = NanotimeUtil.currentTime();
266 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
268 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
269 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
270 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
272 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
273 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
275 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
277 final Instruction i = future.get();
278 i.checkedExecutionStart();
280 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
282 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
283 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
284 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Unknown);
285 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Cancelled);
288 // TODO test deadline with state Queued
291 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
292 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
293 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
295 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
296 final ListenableFuture<Instruction> future2 = this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
298 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
300 Instruction i = future.get();
301 i.checkedExecutionStart();
302 i.executionCompleted(InstructionStatus.Successful, getDetails());
304 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
305 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
306 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
307 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Scheduled);
310 i.checkedExecutionStart();
311 i.executionCompleted(InstructionStatus.Successful, getDetails());
313 this.mockedNotificationServiceWrapper.assertNotificationsCount(6);
314 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(), InstructionStatus.Executing);
315 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(), InstructionStatus.Successful);
318 private Details getDetails() {
319 return new DetailsBuilder().build();
322 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final String... dependencyIds) {
323 return getMockedSubmitInstructionInput(id, Optional.empty(), dependencyIds);
326 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final Optional<Nanotime> deadline, final String... dependencyIds) {
327 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
329 doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
330 final List<InstructionId> dependencies = Lists.newArrayList();
331 for (final String dependencyId : dependencyIds) {
332 dependencies.add(getInstructionId(dependencyId));
335 doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
336 doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
337 doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))).when(mockedSubmitInstruction).getDeadline();
338 return mockedSubmitInstruction;
341 private CancelInstructionInput getCancelInstruction(final String instructionId) {
342 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
343 builder.setId(getInstructionId(instructionId));
344 return builder.build();
347 private InstructionId getInstructionId(final String id) {
348 return new InstructionId(id);
351 private boolean assertInstructionExists(final InstructionId id) {
353 return getDataBroker().newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(InstructionsQueue.class, INSTRUCTIONS_QUEUE_KEY).build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.Instruction.class,
354 new InstructionKey(id))).get().isPresent();
355 } catch (InterruptedException | ExecutionException e) {