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.Matchers.any;
16 import static org.mockito.Mockito.doAnswer;
17 import static org.mockito.Mockito.doNothing;
18 import static org.mockito.Mockito.doReturn;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.MockitoAnnotations.initMocks;
21 import static org.opendaylight.protocol.util.CheckUtil.checkNotPresentOperational;
22 import static org.opendaylight.protocol.util.CheckUtil.checkPresentOperational;
24 import com.google.common.collect.Lists;
25 import com.google.common.util.concurrent.ListenableFuture;
26 import io.netty.util.HashedWheelTimer;
27 import io.netty.util.Timer;
28 import java.math.BigInteger;
29 import java.util.List;
30 import java.util.Optional;
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.mockito.Mock;
35 import org.mockito.Mockito;
36 import org.opendaylight.bgpcep.programming.NanotimeUtil;
37 import org.opendaylight.bgpcep.programming.spi.Instruction;
38 import org.opendaylight.bgpcep.programming.spi.SchedulerException;
39 import org.opendaylight.controller.md.sal.binding.test.AbstractConcurrentDataBrokerTest;
40 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
41 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
42 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
43 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
44 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInputBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueue;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueueKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.Nanotime;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.ProgrammingService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.SubmitInstructionInput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.InstructionKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
63 import org.opendaylight.yangtools.yang.common.RpcResult;
65 public class ProgrammingServiceImplTest extends AbstractConcurrentDataBrokerTest {
67 private static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
68 private static final String INSTRUCTIONS_QUEUE_KEY = "test-instraction-queue";
69 private final Timer timer = new HashedWheelTimer();
70 private MockedExecutorWrapper mockedExecutorWrapper;
71 private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
72 private ProgrammingServiceImpl testedProgrammingService;
74 private ClusterSingletonServiceProvider cssp;
76 private ClusterSingletonServiceRegistration singletonServiceRegistration;
78 private RpcProviderRegistry rpcRegistry;
80 private RoutedRpcRegistration<ProgrammingService> registration;
81 private ClusterSingletonService singletonService;
84 public void setUp() throws Exception {
86 doAnswer(invocationOnMock -> {
87 this.singletonService = (ClusterSingletonService) invocationOnMock.getArguments()[0];
88 return this.singletonServiceRegistration;
89 }).when(this.cssp).registerClusterSingletonService(any(ClusterSingletonService.class));
91 doAnswer(invocationOnMock -> {
92 this.singletonService.closeServiceInstance();
94 }).when(this.singletonServiceRegistration).close();
95 doReturn(this.registration).when(this.rpcRegistry).addRpcImplementation(Mockito.any(),
96 Mockito.any(ProgrammingService.class));
97 doNothing().when(this.registration).close();
98 this.mockedExecutorWrapper = new MockedExecutorWrapper();
99 this.mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
101 this.testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(),
102 this.mockedNotificationServiceWrapper.getMockedNotificationService(),
103 this.mockedExecutorWrapper.getMockedExecutor(), this.rpcRegistry, this.cssp, this.timer,
104 INSTRUCTIONS_QUEUE_KEY);
105 this.singletonService.instantiateServiceInstance();
109 public void tearDown() throws Exception {
110 this.singletonService.closeServiceInstance();
111 this.testedProgrammingService.close();
115 public void testScheduleInstruction() throws Exception {
116 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
117 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
119 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
121 // assert Schedule to executor
122 this.mockedExecutorWrapper.assertSubmittedTasksSize(1);
124 // assert Notification
125 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
126 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(),
127 InstructionStatus.Scheduled);
131 public void testScheduleDependingInstruction() throws Exception {
132 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
133 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
135 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
137 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
139 // First is in state scheduled, so second could not be scheduled yet
140 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
144 public void testScheduleDependingInstructionToFail() throws Exception {
146 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit",
148 } catch (final SchedulerException e) {
149 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
150 this.mockedNotificationServiceWrapper.assertNotificationsCount(0);
153 fail("Instruction schedule should fail on unresolved dependencies");
157 public void testCancelInstruction() throws Exception {
158 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
159 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
160 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
162 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
163 this.testedProgrammingService.cancelInstruction(mockedCancel);
165 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
166 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
168 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
169 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(),
170 InstructionStatus.Cancelled);
174 public void testCancelDependantInstruction() throws Exception {
175 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
176 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
177 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
179 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
180 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3",
181 "mockedSubmit1", "mockedSubmit2");
182 this.testedProgrammingService.scheduleInstruction(mockedSubmit3);
184 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
186 this.mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/+ 3 /*First and all dependencies cancelled*/);
187 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(),
188 InstructionStatus.Scheduled);
189 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
190 InstructionStatus.Cancelled);
191 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(),
192 InstructionStatus.Cancelled);
193 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(),
194 InstructionStatus.Cancelled);
196 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
197 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
198 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit3.getId()));
202 public void testCleanInstructions() throws Exception {
203 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
204 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
205 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
207 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
209 final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
210 final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
211 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
213 ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = this.testedProgrammingService
214 .cleanInstructions(cleanInstructionsInput);
216 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
218 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
220 cleanedInstructionOutput = this.testedProgrammingService.cleanInstructions(cleanInstructionsInput);
221 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
223 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
224 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
227 private void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>>
228 cleanedInstructionOutput, final int unflushedCount) throws InterruptedException,
229 java.util.concurrent.ExecutionException {
230 if (unflushedCount == 0) {
231 final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
232 assertTrue(unflushed == null || unflushed.isEmpty());
234 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
236 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
240 public void testCloseProgrammingService() throws Exception {
241 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
242 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
243 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
245 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
247 this.testedProgrammingService.close();
249 this.mockedNotificationServiceWrapper
250 .assertNotificationsCount(1/* First scheduled */+ 2/* Both cancelled at close */);
253 @Test(timeout = 30 * 1000)
254 public void testTimeoutWhileScheduledTransaction() throws Exception {
255 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 *
256 INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
257 final Nanotime current = NanotimeUtil.currentTime();
258 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
260 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
261 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
262 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
264 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
268 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
269 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
270 InstructionStatus.Cancelled);
273 @Test(timeout = 30 * 1000)
274 public void testTimeoutWhileSuccessfulTransaction() throws Exception {
275 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 *
276 INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
277 final Nanotime current = NanotimeUtil.currentTime();
278 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
280 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
281 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
282 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
284 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
286 final Instruction i = future.get();
287 i.checkedExecutionStart();
288 i.executionCompleted(InstructionStatus.Successful, getDetails());
290 this.mockedNotificationServiceWrapper.assertNotificationsCount(3);
291 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
292 InstructionStatus.Executing);
293 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
294 InstructionStatus.Successful);
295 // Timeout in success should not do anything
298 @Test(timeout = 30 * 1000)
299 public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
300 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 *
301 INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
302 final Nanotime current = NanotimeUtil.currentTime();
303 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
305 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
306 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
307 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
309 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
311 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
313 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
315 final Instruction i = future.get();
316 i.checkedExecutionStart();
318 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
319 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
320 InstructionStatus.Executing);
321 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
322 InstructionStatus.Unknown);
323 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
324 InstructionStatus.Cancelled);
327 // TODO test deadline with state Queued
330 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
331 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
332 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
334 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
335 final ListenableFuture<Instruction> future2 = this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
337 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
339 Instruction i = future.get();
340 i.checkedExecutionStart();
341 i.executionCompleted(InstructionStatus.Successful, getDetails());
343 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
344 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
345 InstructionStatus.Executing);
346 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
347 InstructionStatus.Successful);
348 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
349 InstructionStatus.Scheduled);
352 i.checkedExecutionStart();
353 i.executionCompleted(InstructionStatus.Successful, getDetails());
355 this.mockedNotificationServiceWrapper.assertNotificationsCount(6);
356 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(),
357 InstructionStatus.Executing);
358 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(),
359 InstructionStatus.Successful);
362 private Details getDetails() {
363 return new DetailsBuilder().build();
366 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final String... dependencyIds) {
367 return getMockedSubmitInstructionInput(id, Optional.empty(), dependencyIds);
370 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final Optional<Nanotime> deadline,
371 final String... dependencyIds) {
372 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
374 doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
375 final List<InstructionId> dependencies = Lists.newArrayList();
376 for (final String dependencyId : dependencyIds) {
377 dependencies.add(getInstructionId(dependencyId));
380 doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
381 doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
382 doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE)))
383 .when(mockedSubmitInstruction).getDeadline();
384 return mockedSubmitInstruction;
387 private CancelInstructionInput getCancelInstruction(final String instructionId) {
388 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
389 builder.setId(getInstructionId(instructionId));
390 return builder.build();
393 private InstructionId getInstructionId(final String id) {
394 return new InstructionId(id);
397 private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.
398 rev150720.instruction.queue.Instruction, InstructionKey> buildInstructionIID(final InstructionId id) {
399 return InstanceIdentifier.builder(InstructionsQueue.class, new InstructionsQueueKey(INSTRUCTIONS_QUEUE_KEY))
400 .build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720
401 .instruction.queue.Instruction.class, new InstructionKey(id));