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;
18 import com.google.common.base.Optional;
19 import com.google.common.collect.Lists;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import io.netty.util.HashedWheelTimer;
22 import io.netty.util.Timer;
23 import java.io.IOException;
24 import java.math.BigInteger;
25 import java.util.List;
26 import java.util.concurrent.ExecutionException;
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.controller.md.sal.binding.test.AbstractDataBrokerTest;
34 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelInstructionInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelInstructionInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CleanInstructionsOutput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionStatus;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionsQueue;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.Nanotime;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.SubmitInstructionInput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.InstructionKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.Details;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.DetailsBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
53 public class ProgrammingServiceImplTest extends AbstractDataBrokerTest {
55 public static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
57 private MockedExecutorWrapper mockedExecutorWrapper;
58 private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
59 private ProgrammingServiceImpl testedProgrammingService;
60 private final Timer timer = new HashedWheelTimer();
63 public void setUp() throws IOException, YangSyntaxErrorException {
64 mockedExecutorWrapper = new MockedExecutorWrapper();
65 mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
67 testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(), mockedNotificationServiceWrapper.getMockedNotificationService(), mockedExecutorWrapper.getMockedExecutor(), timer);
71 public void tearDown() throws Exception {
75 public void testScheduleInstruction() throws Exception {
76 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
77 testedProgrammingService.scheduleInstruction(mockedSubmit);
79 assertTrue(assertInstructionExists(mockedSubmit.getId()));
81 // assert Schedule to executor
82 mockedExecutorWrapper.assertSubmittedTasksSize(1);
84 // assert Notification
85 mockedNotificationServiceWrapper.assertNotificationsCount(1);
86 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(), InstructionStatus.Scheduled);
90 public void testScheduleDependingInstruction() throws Exception {
91 testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
92 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
93 testedProgrammingService.scheduleInstruction(mockedSubmit2);
95 mockedExecutorWrapper.assertSubmittedTasksSize(2);
97 // First is in state scheduled, so second could not be scheduled yet
98 mockedNotificationServiceWrapper.assertNotificationsCount(1);
102 public void testScheduleDependingInstructionToFail() throws Exception {
104 testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit", "dep1"));
105 } catch (final SchedulerException e) {
106 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
107 mockedNotificationServiceWrapper.assertNotificationsCount(0);
110 fail("Instruction schedule should fail on unresolved dependencies");
114 public void testCancelInstruction() throws Exception {
115 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
116 testedProgrammingService.scheduleInstruction(mockedSubmit);
118 assertTrue(assertInstructionExists(mockedSubmit.getId()));
120 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
121 testedProgrammingService.cancelInstruction(mockedCancel);
123 assertTrue(assertInstructionExists(mockedSubmit.getId()));
125 mockedExecutorWrapper.assertSubmittedTasksSize(2);
127 mockedNotificationServiceWrapper.assertNotificationsCount(2);
128 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(), InstructionStatus.Cancelled);
132 public void testCancelDependantInstruction() throws Exception {
133 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
134 testedProgrammingService.scheduleInstruction(mockedSubmit1);
135 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
136 testedProgrammingService.scheduleInstruction(mockedSubmit2);
137 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3", "mockedSubmit1", "mockedSubmit2");
138 testedProgrammingService.scheduleInstruction(mockedSubmit3);
140 testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
142 mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/+ 3 /*First and all dependencies cancelled*/);
143 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(), InstructionStatus.Scheduled);
144 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
145 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(), InstructionStatus.Cancelled);
146 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(), InstructionStatus.Cancelled);
148 assertTrue(assertInstructionExists(mockedSubmit1.getId()));
149 assertTrue(assertInstructionExists(mockedSubmit2.getId()));
150 assertTrue(assertInstructionExists(mockedSubmit3.getId()));
154 public void testCleanInstructions() throws Exception {
155 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
156 testedProgrammingService.scheduleInstruction(mockedSubmit1);
157 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
158 testedProgrammingService.scheduleInstruction(mockedSubmit2);
160 final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
161 final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
162 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
164 ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
166 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
168 testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
170 cleanedInstructionOutput = testedProgrammingService.cleanInstructions(cleanInstructionsInput);
171 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
173 assertFalse(assertInstructionExists(mockedSubmit1.getId()));
174 assertFalse(assertInstructionExists(mockedSubmit2.getId()));
177 private void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput,
178 final int unflushedCount) throws InterruptedException, java.util.concurrent.ExecutionException {
179 if (unflushedCount == 0) {
180 final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
181 assertTrue(unflushed == null || unflushed.isEmpty());
183 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
185 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
189 public void testCloseProgrammingService() throws Exception {
190 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
191 testedProgrammingService.scheduleInstruction(mockedSubmit1);
192 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
193 testedProgrammingService.scheduleInstruction(mockedSubmit2);
195 testedProgrammingService.close();
197 mockedNotificationServiceWrapper.assertNotificationsCount(1/* First scheduled */+ 2/* Both cancelled at close */);
200 @Test(timeout = 30 * 1000)
201 public void testTimeoutWhileScheduledTransaction() throws Exception {
202 final BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
203 final Nanotime current = NanotimeUtil.currentTime();
204 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
206 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
207 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
208 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
210 mockedNotificationServiceWrapper.assertNotificationsCount(1);
214 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
216 mockedNotificationServiceWrapper.assertNotificationsCount(2);
217 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Cancelled);
220 @Test(timeout = 30 * 1000)
221 public void testTimeoutWhileSuccessfulTransaction() throws Exception {
222 final BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
223 final Nanotime current = NanotimeUtil.currentTime();
224 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
226 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
227 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
228 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
230 mockedNotificationServiceWrapper.assertNotificationsCount(1);
232 final Instruction i = future.get();
233 i.checkedExecutionStart();
234 i.executionCompleted(InstructionStatus.Successful, getDetails());
236 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
238 mockedNotificationServiceWrapper.assertNotificationsCount(3);
239 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
240 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
241 // Timeout in success should not do anything
244 @Test(timeout = 30 * 1000)
245 public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
246 final BigInteger deadlineOffset = BigInteger.valueOf(1000l * 1000 * 1000 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
247 final Nanotime current = NanotimeUtil.currentTime();
248 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
250 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
251 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
252 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
254 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
255 testedProgrammingService.scheduleInstruction(mockedSubmit2);
257 mockedNotificationServiceWrapper.assertNotificationsCount(1);
259 final Instruction i = future.get();
260 i.checkedExecutionStart();
262 Thread.sleep(2 * INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS * 1000);
264 mockedNotificationServiceWrapper.assertNotificationsCount(4);
265 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
266 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Unknown);
267 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Cancelled);
270 // TODO test deadline with state Queued
273 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
274 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
275 final ListenableFuture<Instruction> future = testedProgrammingService.scheduleInstruction(mockedSubmit1);
277 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
278 final ListenableFuture<Instruction> future2 = testedProgrammingService.scheduleInstruction(mockedSubmit2);
280 mockedNotificationServiceWrapper.assertNotificationsCount(1);
282 Instruction i = future.get();
283 i.checkedExecutionStart();
284 i.executionCompleted(InstructionStatus.Successful, getDetails());
286 mockedNotificationServiceWrapper.assertNotificationsCount(4);
287 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(), InstructionStatus.Executing);
288 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(), InstructionStatus.Successful);
289 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(), InstructionStatus.Scheduled);
292 i.checkedExecutionStart();
293 i.executionCompleted(InstructionStatus.Successful, getDetails());
295 mockedNotificationServiceWrapper.assertNotificationsCount(6);
296 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(), InstructionStatus.Executing);
297 mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(), InstructionStatus.Successful);
300 private Details getDetails() {
301 return new DetailsBuilder().build();
304 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final String... dependencyIds) {
305 return getMockedSubmitInstructionInput(id, Optional.<Nanotime> absent(), dependencyIds);
308 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final Optional<Nanotime> deadline, final String... dependencyIds) {
309 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
311 doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
312 final List<InstructionId> dependencies = Lists.newArrayList();
313 for (String dependencyId : dependencyIds) {
314 dependencies.add(getInstructionId(dependencyId));
317 doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
318 doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
319 doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE))).when(mockedSubmitInstruction).getDeadline();
320 return mockedSubmitInstruction;
323 private CancelInstructionInput getCancelInstruction(final String instructionId) {
324 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
325 builder.setId(getInstructionId(instructionId));
326 return builder.build();
329 private InstructionId getInstructionId(final String id) {
330 return new InstructionId(id);
333 private boolean assertInstructionExists(final InstructionId id) {
335 return getDataBroker().newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(InstructionsQueue.class).build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.queue.Instruction.class,
336 new InstructionKey(id))).get().isPresent();
337 } catch (InterruptedException | ExecutionException e) {