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.mockito.MockitoAnnotations;
37 import org.opendaylight.bgpcep.programming.NanotimeUtil;
38 import org.opendaylight.bgpcep.programming.spi.Instruction;
39 import org.opendaylight.bgpcep.programming.spi.SchedulerException;
40 import org.opendaylight.controller.md.sal.binding.test.AbstractConcurrentDataBrokerTest;
41 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
42 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
43 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
44 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
45 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelInstructionInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CleanInstructionsOutput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueue;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionsQueueKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.Nanotime;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.ProgrammingService;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.SubmitInstructionInput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.queue.InstructionKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev131030.PcepUpdateTunnelInput;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
64 import org.opendaylight.yangtools.yang.common.RpcResult;
66 public class ProgrammingServiceImplTest extends AbstractConcurrentDataBrokerTest {
68 private static final int INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS = 3;
69 private static final String INSTRUCTIONS_QUEUE_KEY = "test-instraction-queue";
70 private final Timer timer = new HashedWheelTimer();
71 private MockedExecutorWrapper mockedExecutorWrapper;
72 private MockedNotificationServiceWrapper mockedNotificationServiceWrapper;
73 private ProgrammingServiceImpl testedProgrammingService;
75 private ClusterSingletonServiceProvider cssp;
77 private ClusterSingletonServiceRegistration singletonServiceRegistration;
79 private RpcProviderRegistry rpcRegistry;
81 private RoutedRpcRegistration<ProgrammingService> registration;
82 private ClusterSingletonService singletonService;
85 public void setUp() throws Exception {
87 doAnswer(invocationOnMock -> {
88 this.singletonService = (ClusterSingletonService) invocationOnMock.getArguments()[0];
89 return this.singletonServiceRegistration;
90 }).when(this.cssp).registerClusterSingletonService(any(ClusterSingletonService.class));
92 doAnswer(invocationOnMock -> {
93 this.singletonService.closeServiceInstance();
95 }).when(this.singletonServiceRegistration).close();
96 doReturn(this.registration).when(this.rpcRegistry).addRpcImplementation(Mockito.any(),
97 Mockito.any(ProgrammingService.class));
98 doNothing().when(this.registration).close();
99 this.mockedExecutorWrapper = new MockedExecutorWrapper();
100 this.mockedNotificationServiceWrapper = new MockedNotificationServiceWrapper();
102 this.testedProgrammingService = new ProgrammingServiceImpl(getDataBroker(),
103 this.mockedNotificationServiceWrapper.getMockedNotificationService(),
104 this.mockedExecutorWrapper.getMockedExecutor(), this.rpcRegistry, this.cssp, this.timer,
105 INSTRUCTIONS_QUEUE_KEY);
106 this.singletonService.instantiateServiceInstance();
110 public void tearDown() throws Exception {
111 this.singletonService.closeServiceInstance();
112 this.testedProgrammingService.close();
116 public void testScheduleInstruction() throws Exception {
117 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
118 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
120 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
122 // assert Schedule to executor
123 this.mockedExecutorWrapper.assertSubmittedTasksSize(1);
125 // assert Notification
126 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
127 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit.getId(),
128 InstructionStatus.Scheduled);
132 public void testScheduleDependingInstruction() throws Exception {
133 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit1"));
134 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
136 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
138 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
140 // First is in state scheduled, so second could not be scheduled yet
141 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
145 public void testScheduleDependingInstructionToFail() throws Exception {
147 this.testedProgrammingService.scheduleInstruction(getMockedSubmitInstructionInput("mockedSubmit",
149 } catch (final SchedulerException e) {
150 assertThat(e.getMessage(), containsString("Unknown dependency ID"));
151 this.mockedNotificationServiceWrapper.assertNotificationsCount(0);
154 fail("Instruction schedule should fail on unresolved dependencies");
158 public void testCancelInstruction() throws Exception {
159 final SubmitInstructionInput mockedSubmit = getMockedSubmitInstructionInput("mockedSubmit");
160 this.testedProgrammingService.scheduleInstruction(mockedSubmit);
161 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
163 final CancelInstructionInput mockedCancel = getCancelInstruction("mockedSubmit");
164 this.testedProgrammingService.cancelInstruction(mockedCancel);
166 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit.getId()));
167 this.mockedExecutorWrapper.assertSubmittedTasksSize(2);
169 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
170 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit.getId(),
171 InstructionStatus.Cancelled);
175 public void testCancelDependantInstruction() throws Exception {
176 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
177 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
178 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
180 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
181 final SubmitInstructionInput mockedSubmit3 = getMockedSubmitInstructionInput("mockedSubmit3",
182 "mockedSubmit1", "mockedSubmit2");
183 this.testedProgrammingService.scheduleInstruction(mockedSubmit3);
185 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
187 this.mockedNotificationServiceWrapper.assertNotificationsCount(1 /*First Scheduled*/+ 3 /*First and all dependencies cancelled*/);
188 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(0, mockedSubmit1.getId(),
189 InstructionStatus.Scheduled);
190 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
191 InstructionStatus.Cancelled);
192 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit2.getId(),
193 InstructionStatus.Cancelled);
194 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit3.getId(),
195 InstructionStatus.Cancelled);
197 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
198 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
199 checkPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit3.getId()));
203 public void testCleanInstructions() throws Exception {
204 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
205 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
206 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
208 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
210 final CleanInstructionsInputBuilder cleanInstructionsInputBuilder = new CleanInstructionsInputBuilder();
211 final CleanInstructionsInput cleanInstructionsInput = cleanInstructionsInputBuilder.setId(
212 Lists.newArrayList(mockedSubmit1.getId(), mockedSubmit2.getId())).build();
214 ListenableFuture<RpcResult<CleanInstructionsOutput>> cleanedInstructionOutput = this.testedProgrammingService
215 .cleanInstructions(cleanInstructionsInput);
217 assertCleanInstructionOutput(cleanedInstructionOutput, 2);
219 this.testedProgrammingService.cancelInstruction(getCancelInstruction("mockedSubmit1"));
221 cleanedInstructionOutput = this.testedProgrammingService.cleanInstructions(cleanInstructionsInput);
222 assertCleanInstructionOutput(cleanedInstructionOutput, 0);
224 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit1.getId()));
225 checkNotPresentOperational(getDataBroker(), buildInstructionIID(mockedSubmit2.getId()));
228 private void assertCleanInstructionOutput(final ListenableFuture<RpcResult<CleanInstructionsOutput>>
229 cleanedInstructionOutput, final int unflushedCount) throws InterruptedException,
230 java.util.concurrent.ExecutionException {
231 if (unflushedCount == 0) {
232 final List<InstructionId> unflushed = cleanedInstructionOutput.get().getResult().getUnflushed();
233 assertTrue(unflushed == null || unflushed.isEmpty());
235 assertEquals(unflushedCount, cleanedInstructionOutput.get().getResult().getUnflushed().size());
237 assertEquals(0, cleanedInstructionOutput.get().getErrors().size());
241 public void testCloseProgrammingService() throws Exception {
242 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
243 this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
244 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
246 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
248 this.testedProgrammingService.close();
250 this.mockedNotificationServiceWrapper
251 .assertNotificationsCount(1/* First scheduled */+ 2/* Both cancelled at close */);
254 @Test(timeout = 30 * 1000)
255 public void testTimeoutWhileScheduledTransaction() throws Exception {
256 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 *
257 INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
258 final Nanotime current = NanotimeUtil.currentTime();
259 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
261 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
262 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
263 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
265 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
269 this.mockedNotificationServiceWrapper.assertNotificationsCount(2);
270 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
271 InstructionStatus.Cancelled);
274 @Test(timeout = 30 * 1000)
275 public void testTimeoutWhileSuccessfulTransaction() throws Exception {
276 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 *
277 INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
278 final Nanotime current = NanotimeUtil.currentTime();
279 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
281 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
282 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
283 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
285 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
287 final Instruction i = future.get();
288 i.checkedExecutionStart();
289 i.executionCompleted(InstructionStatus.Successful, getDetails());
291 this.mockedNotificationServiceWrapper.assertNotificationsCount(3);
292 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
293 InstructionStatus.Executing);
294 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
295 InstructionStatus.Successful);
296 // Timeout in success should not do anything
299 @Test(timeout = 30 * 1000)
300 public void testTimeoutWhileExecutingWithDependenciesTransaction() throws Exception {
301 final BigInteger deadlineOffset = BigInteger.valueOf(1000L * 1000 * 1000 *
302 INSTRUCTION_DEADLINE_OFFSET_IN_SECONDS /* seconds */);
303 final Nanotime current = NanotimeUtil.currentTime();
304 final Nanotime deadlineNano = new Nanotime(current.getValue().add(deadlineOffset));
306 final Optional<Nanotime> deadline = Optional.of(deadlineNano);
307 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1", deadline);
308 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
310 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2",
312 this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
314 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
316 final Instruction i = future.get();
317 i.checkedExecutionStart();
319 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
320 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
321 InstructionStatus.Executing);
322 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
323 InstructionStatus.Unknown);
324 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
325 InstructionStatus.Cancelled);
328 // TODO test deadline with state Queued
331 public void testSuccessExecutingWithDependenciesTransaction() throws Exception {
332 final SubmitInstructionInput mockedSubmit1 = getMockedSubmitInstructionInput("mockedSubmit1");
333 final ListenableFuture<Instruction> future = this.testedProgrammingService.scheduleInstruction(mockedSubmit1);
335 final SubmitInstructionInput mockedSubmit2 = getMockedSubmitInstructionInput("mockedSubmit2", "mockedSubmit1");
336 final ListenableFuture<Instruction> future2 = this.testedProgrammingService.scheduleInstruction(mockedSubmit2);
338 this.mockedNotificationServiceWrapper.assertNotificationsCount(1);
340 Instruction i = future.get();
341 i.checkedExecutionStart();
342 i.executionCompleted(InstructionStatus.Successful, getDetails());
344 this.mockedNotificationServiceWrapper.assertNotificationsCount(4);
345 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(1, mockedSubmit1.getId(),
346 InstructionStatus.Executing);
347 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(2, mockedSubmit1.getId(),
348 InstructionStatus.Successful);
349 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(3, mockedSubmit2.getId(),
350 InstructionStatus.Scheduled);
353 i.checkedExecutionStart();
354 i.executionCompleted(InstructionStatus.Successful, getDetails());
356 this.mockedNotificationServiceWrapper.assertNotificationsCount(6);
357 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(4, mockedSubmit2.getId(),
358 InstructionStatus.Executing);
359 this.mockedNotificationServiceWrapper.assertInstructionStatusChangedNotification(5, mockedSubmit2.getId(),
360 InstructionStatus.Successful);
363 private Details getDetails() {
364 return new DetailsBuilder().build();
367 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final String... dependencyIds) {
368 return getMockedSubmitInstructionInput(id, Optional.empty(), dependencyIds);
371 private SubmitInstructionInput getMockedSubmitInstructionInput(final String id, final Optional<Nanotime> deadline,
372 final String... dependencyIds) {
373 final SubmitInstructionInput mockedSubmitInstruction = mock(SubmitInstructionInput.class);
375 doReturn(PcepUpdateTunnelInput.class).when(mockedSubmitInstruction).getImplementedInterface();
376 final List<InstructionId> dependencies = Lists.newArrayList();
377 for (final String dependencyId : dependencyIds) {
378 dependencies.add(getInstructionId(dependencyId));
381 doReturn(dependencies).when(mockedSubmitInstruction).getPreconditions();
382 doReturn(getInstructionId(id)).when(mockedSubmitInstruction).getId();
383 doReturn(deadline.isPresent() ? deadline.get() : new Nanotime(BigInteger.valueOf(Long.MAX_VALUE)))
384 .when(mockedSubmitInstruction).getDeadline();
385 return mockedSubmitInstruction;
388 private CancelInstructionInput getCancelInstruction(final String instructionId) {
389 final CancelInstructionInputBuilder builder = new CancelInstructionInputBuilder();
390 builder.setId(getInstructionId(instructionId));
391 return builder.build();
394 private InstructionId getInstructionId(final String id) {
395 return new InstructionId(id);
398 private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.
399 rev150720.instruction.queue.Instruction, InstructionKey> buildInstructionIID(final InstructionId id) {
400 return InstanceIdentifier.builder(InstructionsQueue.class, new InstructionsQueueKey(INSTRUCTIONS_QUEUE_KEY))
401 .build().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720
402 .instruction.queue.Instruction.class, new InstructionKey(id));