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.doReturn;
17 import static org.mockito.Mockito.mock;
19 import com.google.common.base.Optional;
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.io.IOException;
25 import java.math.BigInteger;
26 import java.util.List;
27 import java.util.concurrent.ExecutionException;
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.controller.md.sal.binding.test.AbstractDataBrokerTest;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueue;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueueKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.Nanotime;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.SubmitInstructionInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.InstructionKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.common.RpcResult;
53 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
55 public class ProgrammingServiceImplTest extends AbstractDataBrokerTest {
57 public static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
58 private static final InstructionsQueueKey INSTRUCTIONS_QUEUE_KEY = new InstructionsQueueKey("test-instraction-queue");
60 private MockedExecutorWrapper mockedExecutorWrapper;
61 private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
62 private ProgrammingServiceImpl testedProgrammingService;
63 private final Timer timer = new HashedWheelTimer();
66 public void setUp() throws IOException, YangSyntaxErrorException {
67 mockedExecutorWrapper = new MockedExecutorWrapper();
68 mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
70 testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(),
71 mockedNotificationServiceWrapper.getMockedNotificationService(),
72 mockedExecutorWrapper.getMockedExecutor(), timer, INSTRUCTIONS_QUEUE_KEY);
76 public void tearDown() throws Exception {
80 public void testScheduleInstruction() throws Exception {
81 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
82 testedProgrammingService.scheduleInstruction(mockedSubmit);
84 assertTrue(assertInstructionExists(mockedSubmit.getId()));
86 // assert Schedule to executor
87 mockedExecutorWrapper.assertSubmittedTasksSize(1);
89 // assert Notification
90 mockedNotificationServiceWrapper.assertNotificationsCount(1);
91 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(), InstructionStatus.Scheduled);
95 public void testScheduleDependingInstruction() throws Exception {
96 testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
97 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
98 testedProgrammingService.scheduleInstruction(mockedSubmit2);
100 mockedExecutorWrapper.assertSubmittedTasksSize(2);
102 // First is in state scheduled, so second could not be scheduled yet
103 mockedNotificationServiceWrapper.assertNotificationsCount(1);
107 public void testScheduleDependingInstructionToFail() throws Exception {
109 testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit", "dep1"));
110 } catch (final SchedulerException e) {
111 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
112 mockedNotificationServiceWrapper.assertNotificationsCount(0);
115 fail("Instruction schedule should fail on unresolved dependencies");
119 public void testCancelInstruction() throws Exception {
120 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
121 testedProgrammingService.scheduleInstruction(mockedSubmit);
123 assertTrue(assertInstructionExists(mockedSubmit.getId()));
125 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
126 testedProgrammingService.cancelInstruction(mockedCancel);
128 assertTrue(assertInstructionExists(mockedSubmit.getId()));
130 mockedExecutorWrapper.assertSubmittedTasksSize(2);
132 mockedNotificationServiceWrapper.assertNotificationsCount(2);
133 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(), InstructionStatus.Cancelled);
137 public void testCancelDependantInstruction() throws Exception {
138 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
139 testedProgrammingService.scheduleInstruction(mockedSubmit1);
140 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
141 testedProgrammingService.scheduleInstruction(mockedSubmit2);
142 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3", "mockedSubmit1", "mockedSubmit2");
143 testedProgrammingService.scheduleInstruction(mockedSubmit3);
145 testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
147 mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/+ 3 /*First and all dependencies cancelled*/);
148 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(), InstructionStatus.Scheduled);
149 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
150 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(), InstructionStatus.Cancelled);
151 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(), InstructionStatus.Cancelled);
153 assertTrue(assertInstructionExists(mockedSubmit1.getId()));
154 assertTrue(assertInstructionExists(mockedSubmit2.getId()));
155 assertTrue(assertInstructionExists(mockedSubmit3.getId()));
159 public void testCleanInstructions() throws Exception {
160 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
161 testedProgrammingService.scheduleInstruction(mockedSubmit1);
162 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
163 testedProgrammingService.scheduleInstruction(mockedSubmit2);
165 final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
166 final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
167 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
169 ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
171 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
173 testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
175 cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
176 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
178 assertFalse(assertInstructionExists(mockedSubmit1.getId()));
179 assertFalse(assertInstructionExists(mockedSubmit2.getId()));
182 private void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput,
183 final int unflushedCount) throws InterruptedException, java.util.concurrent.ExecutionException {
184 if (unflushedCount == 0) {
185 final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
186 assertTrue(unflushed == null || unflushed.isEmpty());
188 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
190 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
194 public void testCloseProgrammingService() throws Exception {
195 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
196 testedProgrammingService.scheduleInstruction(mockedSubmit1);
197 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
198 testedProgrammingService.scheduleInstruction(mockedSubmit2);
200 testedProgrammingService.close();
202 mockedNotificationServiceWrapper.assertNotificationsCount(1/* First scheduled */+ 2/* Both cancelled at close */);
205 @Test(timeout = 30 * 1000)
206 public void testTimeoutWhileScheduledTransaction() throws Exception {
207 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
208 final Nanotime current = NanotimeUtil.currentTime();
209 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
211 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
212 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
213 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
215 mockedNotificationServiceWrapper.assertNotificationsCount(1);
219 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
221 mockedNotificationServiceWrapper.assertNotificationsCount(2);
222 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
225 @Test(timeout = 30 * 1000)
226 public void testTimeoutWhileSuccessfulTransaction() throws Exception {
227 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
228 final Nanotime current = NanotimeUtil.currentTime();
229 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
231 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
232 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
233 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
235 mockedNotificationServiceWrapper.assertNotificationsCount(1);
237 final Instruction i = future.get();
238 i.checkedExecutionStart();
239 i.executionCompleted(InstructionStatus.Successful, getDetails());
241 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
243 mockedNotificationServiceWrapper.assertNotificationsCount(3);
244 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
245 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
246 // Timeout in success should not do anything
249 @Test(timeout = 30 * 1000)
250 public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
251 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
252 final Nanotime current = NanotimeUtil.currentTime();
253 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
255 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
256 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
257 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
259 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
260 testedProgrammingService.scheduleInstruction(mockedSubmit2);
262 mockedNotificationServiceWrapper.assertNotificationsCount(1);
264 final Instruction i = future.get();
265 i.checkedExecutionStart();
267 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
269 mockedNotificationServiceWrapper.assertNotificationsCount(4);
270 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
271 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Unknown);
272 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Cancelled);
275 // TODO test deadline with state Queued
278 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
279 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
280 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
282 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
283 final ListenableFuture<Instruction> future2 = testedProgrammingService.scheduleInstruction(mockedSubmit2);
285 mockedNotificationServiceWrapper.assertNotificationsCount(1);
287 Instruction i = future.get();
288 i.checkedExecutionStart();
289 i.executionCompleted(InstructionStatus.Successful, getDetails());
291 mockedNotificationServiceWrapper.assertNotificationsCount(4);
292 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
293 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
294 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Scheduled);
297 i.checkedExecutionStart();
298 i.executionCompleted(InstructionStatus.Successful, getDetails());
300 mockedNotificationServiceWrapper.assertNotificationsCount(6);
301 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(), InstructionStatus.Executing);
302 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(), InstructionStatus.Successful);
305 private Details getDetails() {
306 return new DetailsBuilder().build();
309 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final String... dependencyIds) {
310 return getMockedSubmitInstructionInput(id, Optional.absent(), dependencyIds);
313 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final Optional<Nanotime> deadline, final String... dependencyIds) {
314 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
316 doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
317 final List<InstructionId> dependencies = Lists.newArrayList();
318 for (final String dependencyId : dependencyIds) {
319 dependencies.add(getInstructionId(dependencyId));
322 doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
323 doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
324 doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))).when(mockedSubmitInstruction).getDeadline();
325 return mockedSubmitInstruction;
328 private CancelInstructionInput getCancelInstruction(final String instructionId) {
329 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
330 builder.setId(getInstructionId(instructionId));
331 return builder.build();
334 private InstructionId getInstructionId(final String id) {
335 return new InstructionId(id);
338 private boolean assertInstructionExists(final InstructionId id) {
340 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,
341 new InstructionKey(id))).get().isPresent();
342 } catch (InterruptedException | ExecutionException e) {