522d9f57be2e361590b542bbc7b223488848b9e1
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop.eventmanager;
22
23 import static org.assertj.core.api.Assertions.assertThatCode;
24 import static org.assertj.core.api.Assertions.assertThatThrownBy;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertNull;
29 import static org.junit.Assert.assertSame;
30 import static org.junit.Assert.assertTrue;
31 import static org.mockito.ArgumentMatchers.any;
32 import static org.mockito.ArgumentMatchers.anyLong;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.TreeMap;
43 import java.util.UUID;
44 import java.util.concurrent.CompletableFuture;
45 import java.util.concurrent.ExecutorService;
46 import java.util.function.Consumer;
47 import org.drools.core.WorkingMemory;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.kie.api.runtime.rule.FactHandle;
51 import org.mockito.ArgumentCaptor;
52 import org.mockito.Mock;
53 import org.mockito.MockitoAnnotations;
54 import org.onap.policy.common.utils.coder.Coder;
55 import org.onap.policy.common.utils.coder.CoderException;
56 import org.onap.policy.common.utils.coder.StandardYamlCoder;
57 import org.onap.policy.common.utils.io.Serializer;
58 import org.onap.policy.common.utils.resources.ResourceUtils;
59 import org.onap.policy.controlloop.ControlLoopEventStatus;
60 import org.onap.policy.controlloop.ControlLoopException;
61 import org.onap.policy.controlloop.ControlLoopNotificationType;
62 import org.onap.policy.controlloop.ControlLoopOperation;
63 import org.onap.policy.controlloop.ControlLoopTargetType;
64 import org.onap.policy.controlloop.VirtualControlLoopEvent;
65 import org.onap.policy.controlloop.VirtualControlLoopNotification;
66 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
67 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
68 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
69 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
70 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2.NewEventStatus;
71 import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager2.State;
72 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
73 import org.onap.policy.controlloop.policy.Policy;
74 import org.onap.policy.controlloop.policy.PolicyResult;
75 import org.onap.policy.controlloop.policy.Target;
76 import org.onap.policy.controlloop.policy.TargetType;
77 import org.onap.policy.drools.core.lock.LockCallback;
78 import org.onap.policy.drools.core.lock.LockImpl;
79 import org.onap.policy.drools.core.lock.LockState;
80 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
81 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
82
83 public class ControlLoopEventManager2Test {
84     private static final UUID REQ_ID = UUID.randomUUID();
85     private static final String CL_NAME = "my-closed-loop-name";
86     private static final String POLICY_NAME = "my-policy-name";
87     private static final String POLICY_SCOPE = "my-scope";
88     private static final String POLICY_VERSION = "1.2.3";
89     private static final String MY_TARGET = "my-target";
90     private static final String LOCK1 = "my-lock-A";
91     private static final String LOCK2 = "my-lock-B";
92     private static final Coder yamlCoder = new StandardYamlCoder();
93
94     @Mock
95     private WorkingMemory workMem;
96     @Mock
97     private Consumer<OperationOutcome> callback1;
98     @Mock
99     private Consumer<OperationOutcome> callback2;
100     @Mock
101     private Consumer<OperationOutcome> callback3;
102     @Mock
103     private FactHandle factHandle;
104     @Mock
105     private ActorService actors;
106     @Mock
107     private OperationHistoryDataManager dataMgr;
108     @Mock
109     private ControlLoopOperationManager2 oper1;
110     @Mock
111     private ControlLoopOperationManager2 oper2;
112     @Mock
113     private ControlLoopOperationManager2 oper3;
114     @Mock
115     private ExecutorService executor;
116
117     private long preCreateTimeMs;
118     private List<LockImpl> locks;
119     private Target target;
120     private ToscaPolicy tosca;
121     private ControlLoopParams params;
122     private VirtualControlLoopEvent event;
123     private int updateCount;
124     private ControlLoopEventManager2 mgr;
125
126     /**
127      * Sets up.
128      */
129     @Before
130     public void setUp() throws ControlLoopException, CoderException {
131         MockitoAnnotations.initMocks(this);
132
133         when(oper1.getHistory()).thenReturn(makeHistory("A"));
134         when(oper2.getHistory()).thenReturn(makeHistory("B"));
135         when(oper3.getHistory()).thenReturn(makeHistory("C"));
136
137         when(oper1.getActor()).thenReturn("First");
138         when(oper1.getOperation()).thenReturn("OperationA");
139         when(oper1.getOperationMessage()).thenReturn("message-A");
140
141         when(oper2.getActor()).thenReturn("Second");
142         when(oper2.getOperation()).thenReturn("OperationB");
143         when(oper2.getOperationMessage()).thenReturn("message-B");
144
145         when(oper3.getActor()).thenReturn("Third");
146         when(oper3.getOperation()).thenReturn("OperationC");
147         when(oper3.getOperationMessage()).thenReturn("message-C");
148
149         when(workMem.getFactHandle(any())).thenReturn(factHandle);
150
151         event = new VirtualControlLoopEvent();
152         event.setRequestId(REQ_ID);
153         event.setTarget(ControlLoopOperationManager2.VSERVER_VSERVER_NAME);
154         event.setAai(new TreeMap<>(Map.of(ControlLoopOperationManager2.VSERVER_VSERVER_NAME, MY_TARGET)));
155         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
156         event.setClosedLoopControlName(CL_NAME);
157         event.setTargetType(TargetType.VNF.toString());
158
159         target = new Target();
160         target.setType(TargetType.VNF);
161
162         params = new ControlLoopParams();
163         params.setClosedLoopControlName(CL_NAME);
164         params.setPolicyName(POLICY_NAME);
165         params.setPolicyScope(POLICY_SCOPE);
166         params.setPolicyVersion(POLICY_VERSION);
167
168         loadPolicy("eventManager/event-mgr-simple.yaml");
169
170         locks = new ArrayList<>();
171
172         updateCount = 0;
173
174         preCreateTimeMs = System.currentTimeMillis();
175
176         mgr = new MyManagerWithOper(params, event, workMem);
177     }
178
179     @Test
180     public void testConstructor() {
181         assertEquals(POLICY_NAME, mgr.getPolicyName());
182
183         Map<String, String> orig = event.getAai();
184
185         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
186         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
187                         .hasMessage("is-closed-loop-disabled is set to true on VServer or VNF");
188
189         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "inactive"));
190         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
191                         .hasMessage("prov-status is not ACTIVE on VServer or VNF");
192
193         // valid
194         event.setAai(orig);
195         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
196
197         // invalid
198         event.setTarget("unknown-target");
199         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
200     }
201
202     /**
203      * Runs through a policy that has several operations.
204      */
205     @Test
206     public void testMultiOperation() throws Exception {
207
208         loadPolicy("eventManager/event-mgr-multi.yaml");
209
210         mgr = new MyManagerWithOper(params, event, workMem);
211         mgr.start();
212
213         for (ControlLoopOperationManager2 oper : Arrays.asList(oper1, oper2, oper3)) {
214             assertTrue(mgr.isActive());
215             nextStep(oper, true, PolicyResult.SUCCESS);
216             runRule();
217
218             assertTrue(mgr.isActive());
219             nextStep(oper, false, PolicyResult.SUCCESS);
220             runRule();
221         }
222
223         assertFalse(mgr.isActive());
224     }
225
226     @Test
227     public void testStart() throws Exception {
228         // start it
229         mgr.start();
230
231         // cannot re-start
232         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
233                         .hasMessage("manager already started");
234     }
235
236     /**
237      * Tests start() error cases.
238      */
239     @Test
240     public void testStartErrors() throws Exception {
241         // wrong jvm
242         ControlLoopEventManager2 mgr2 = new ControlLoopEventManager2(params, event, workMem);
243         ControlLoopEventManager2 mgr3 = Serializer.roundTrip(mgr2);
244         assertThatCode(() -> mgr3.start()).isInstanceOf(IllegalStateException.class)
245                         .hasMessage("manager is no longer active");
246
247         // no fact handle
248         when(workMem.getFactHandle(any())).thenReturn(null);
249         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
250                         .hasMessage("manager is not in working memory");
251     }
252
253     @Test
254     public void testNextStep_testStartOperationSuccess() throws ControlLoopException {
255         runOperation(PolicyResult.SUCCESS);
256
257         VirtualControlLoopNotification notif = mgr.getNotification();
258         assertEquals(ControlLoopNotificationType.FINAL_SUCCESS, notif.getNotification());
259         assertNull(notif.getMessage());
260
261         assertThatCode(() -> mgr.nextStep()).doesNotThrowAnyException();
262     }
263
264     /**
265      * Tests nextStep() when the next step is invalid, which should cause an exception to
266      * be thrown by the processor.
267      */
268     @Test
269     public void testNextStepMissing() throws Exception {
270         mgr.start();
271
272         when(oper1.nextStep()).thenThrow(new IllegalArgumentException("expected exception"));
273
274         mgr.nextStep();
275
276         assertFalse(mgr.isActive());
277
278         VirtualControlLoopNotification notif = mgr.getNotification();
279         assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notif.getNotification());
280         assertEquals("Policy processing aborted due to policy error", notif.getMessage());
281         assertTrue(notif.getHistory().isEmpty());
282     }
283
284     /**
285      * Tests startOperation() with FINAL_FAILURE_EXCEPTION.
286      */
287     @Test
288     public void testStartOperationException() throws ControlLoopException {
289         runOperation(PolicyResult.FAILURE_EXCEPTION);
290
291         VirtualControlLoopNotification notif = mgr.getNotification();
292         assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notif.getNotification());
293         assertEquals("Exception in processing closed loop", notif.getMessage());
294     }
295
296     /**
297      * Tests startOperation() with FINAL_FAILURE.
298      */
299     @Test
300     public void testStartOperationFailure() throws ControlLoopException {
301         runOperation(PolicyResult.FAILURE);
302
303         VirtualControlLoopNotification notif = mgr.getNotification();
304         assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notif.getNotification());
305         assertNull(notif.getMessage());
306     }
307
308     /**
309      * Tests startOperation() with FINAL_OPENLOOP.
310      */
311     @Test
312     public void testStartOperationOpenLoop() throws ControlLoopException {
313         runOperation(PolicyResult.FAILURE_GUARD);
314
315         VirtualControlLoopNotification notif = mgr.getNotification();
316         assertEquals(ControlLoopNotificationType.FINAL_OPENLOOP, notif.getNotification());
317         assertNull(notif.getMessage());
318     }
319
320     @Test
321     public void testIsActive() throws Exception {
322         mgr = new ControlLoopEventManager2(params, event, workMem);
323         assertTrue(mgr.isActive());
324
325         ControlLoopEventManager2 mgr2 = Serializer.roundTrip(mgr);
326         assertFalse(mgr2.isActive());
327     }
328
329     @Test
330     public void testUpdated() throws ControlLoopException {
331         mgr.start();
332
333         // not the active operation - should be ignored
334         mgr.updated(oper3);
335         verify(workMem, never()).update(any(), any());
336
337         VirtualControlLoopNotification notif;
338
339         // check notification data
340         when(oper1.getState()).thenReturn(State.LOCK_DENIED);
341         mgr.updated(oper1);
342         notif = mgr.getNotification();
343         assertNotNull(notif.getHistory());
344
345         /*
346          * try the various cases
347          */
348         when(oper1.getState()).thenReturn(State.LOCK_DENIED);
349         mgr.updated(oper1);
350         verifyNotification(ControlLoopNotificationType.REJECTED, "The target my-target is already locked");
351
352         when(oper1.getState()).thenReturn(State.LOCK_LOST);
353         mgr.updated(oper1);
354         verifyNotification(ControlLoopNotificationType.OPERATION_FAILURE, "The target my-target is no longer locked");
355
356         when(oper1.getState()).thenReturn(State.GUARD_STARTED);
357         mgr.updated(oper1);
358         verifyNotification(ControlLoopNotificationType.OPERATION, "Sending guard query for First OperationA");
359
360         when(oper1.getState()).thenReturn(State.GUARD_PERMITTED);
361         mgr.updated(oper1);
362         verifyNotification(ControlLoopNotificationType.OPERATION, "Guard result for First OperationA is Permit");
363
364         when(oper1.getState()).thenReturn(State.GUARD_DENIED);
365         mgr.updated(oper1);
366         verifyNotification(ControlLoopNotificationType.OPERATION, "Guard result for First OperationA is Deny");
367
368         when(oper1.getState()).thenReturn(State.OPERATION_SUCCESS);
369         mgr.updated(oper1);
370         verifyNotification(ControlLoopNotificationType.OPERATION_SUCCESS, "message-A");
371
372         when(oper1.getState()).thenReturn(State.OPERATION_FAILURE);
373         mgr.updated(oper1);
374         verifyNotification(ControlLoopNotificationType.OPERATION_FAILURE, "message-A");
375
376         // should still be active
377         assertTrue(mgr.isActive());
378
379         /*
380          * control loop time
381          */
382         when(oper1.getState()).thenReturn(State.CONTROL_LOOP_TIMEOUT);
383         mgr.updated(oper1);
384         verifyNotification(ControlLoopNotificationType.FINAL_FAILURE, "Control Loop timed out");
385
386         // should now be done
387         assertFalse(mgr.isActive());
388     }
389
390     @Test
391     public void testDestroy() {
392         mgr.requestLock(LOCK1, callback1);
393         mgr.requestLock(LOCK2, callback2);
394         mgr.requestLock(LOCK1, callback3);
395
396         mgr.destroy();
397
398         freeLocks();
399
400         for (LockImpl lock : locks) {
401             assertTrue(lock.isUnavailable());
402         }
403     }
404
405     /**
406      * Tests destroy() once it has been started.
407      */
408     @Test
409     public void testDestroyStarted() throws ControlLoopException {
410         mgr.start();
411
412         mgr.requestLock(LOCK1, callback1);
413         mgr.requestLock(LOCK2, callback2);
414         mgr.requestLock(LOCK1, callback3);
415
416         mgr.destroy();
417
418         freeLocks();
419
420         // should have canceled the operation
421         verify(oper1).cancel();
422
423         for (LockImpl lock : locks) {
424             assertTrue(lock.isUnavailable());
425         }
426     }
427
428     @Test
429     public void testMakeNotification() throws ControlLoopException {
430         mgr.start();
431
432         nextStep(oper1, true, PolicyResult.SUCCESS);
433         runRule();
434
435         // check notification while running
436         VirtualControlLoopNotification notif = mgr.getNotification();
437         assertEquals("message-A", notif.getMessage());
438
439         List<ControlLoopOperation> history = notif.getHistory();
440         assertNotNull(history);
441
442         nextStep(oper1, false, PolicyResult.SUCCESS);
443         runRule();
444
445         assertFalse(mgr.isActive());
446
447         // check notification when complete
448         notif = mgr.getNotification();
449         assertNull(notif.getMessage());
450         assertEquals(history, notif.getHistory());
451     }
452
453     @Test
454     public void testOnNewEvent() {
455         VirtualControlLoopEvent event2 = new VirtualControlLoopEvent(event);
456         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
457
458         event2.setPayload("other payload");
459         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
460         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
461         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event));
462
463         event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
464         assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
465
466         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
467         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
468
469         event2.setClosedLoopEventStatus(null);
470         assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
471     }
472
473     @Test
474     public void testDetmControlLoopTimeoutMs() throws Exception {
475         verifyTimeout(1200 * 1000L);
476     }
477
478     private void verifyTimeout(long timeMs) {
479         long end = mgr.getEndTimeMs();
480         assertTrue(end >= preCreateTimeMs + timeMs);
481         assertTrue(end < preCreateTimeMs + timeMs + 5000);
482     }
483
484     @Test
485     public void testCheckEventSyntax() {
486         // initially, it's valid
487         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
488
489         event.setTarget("unknown-target");
490         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
491                         .hasMessage("target field invalid");
492
493         event.setTarget(null);
494         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
495                         .hasMessage("No target field");
496
497         // abated supersedes previous errors - so it shouldn't throw an exception
498         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
499         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
500
501         event.setRequestId(null);
502         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
503                         .hasMessage("No request ID");
504
505         event.setClosedLoopControlName(null);
506         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
507                         .hasMessage("No control loop name");
508     }
509
510     @Test
511     public void testValidateStatus() {
512         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
513         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
514
515         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
516         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
517
518         event.setClosedLoopEventStatus(null);
519         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
520                         .hasMessage("Invalid value in closedLoopEventStatus");
521     }
522
523     @Test
524     public void testValidateAaiData() {
525         event.setTargetType("unknown-target-type");
526         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
527                         .hasMessage("The target type is not supported");
528
529         event.setTargetType(null);
530         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
531                         .hasMessage("The Target type is null");
532
533         event.setAai(null);
534         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
535                         .hasMessage("AAI is null");
536
537         // VM case
538         event.setTargetType(ControlLoopTargetType.VM);
539         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
540         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
541
542         event.setAai(Map.of());
543         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
544
545         // VNF case
546         event.setTargetType(ControlLoopTargetType.VNF);
547         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
548         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
549
550         event.setAai(Map.of());
551         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
552
553         // PNF case
554         event.setTargetType(ControlLoopTargetType.PNF);
555         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
556         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
557
558         event.setAai(Map.of());
559         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
560     }
561
562     @Test
563     public void testValidateAaiVmVnfData() {
564         event.setTargetType(ControlLoopTargetType.VM);
565         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
566         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
567
568         event.setAai(Map.of(ControlLoopEventManager2.VSERVER_VSERVER_NAME, MY_TARGET));
569         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
570
571         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_NAME, MY_TARGET));
572         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
573
574         event.setAai(Map.of());
575         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class).hasMessage(
576                         "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
577     }
578
579     @Test
580     public void testValidateAaiPnfData() {
581         event.setTargetType(ControlLoopTargetType.PNF);
582         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
583         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
584
585         event.setAai(Map.of());
586         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
587                         .hasMessage("AAI PNF object key pnf-name is missing");
588     }
589
590     @Test
591     public void testIsClosedLoopDisabled() {
592         Map<String, String> orig = event.getAai();
593
594         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
595         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
596
597         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "true"));
598         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
599
600         event.setAai(addAai(orig, ControlLoopEventManager2.PNF_IS_IN_MAINT, "true"));
601         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
602     }
603
604     private Map<String, String> addAai(Map<String, String> original, String key, String value) {
605         Map<String, String> map = new TreeMap<>(original);
606         map.put(key, value);
607         return map;
608     }
609
610     @Test
611     public void testIsProvStatusInactive() {
612         Map<String, String> orig = event.getAai();
613
614         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "ACTIVE"));
615         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
616
617         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "inactive"));
618         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
619
620         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "ACTIVE"));
621         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
622
623         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "inactive"));
624         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
625     }
626
627     @Test
628     public void testIsAaiTrue() {
629         Map<String, String> orig = event.getAai();
630
631         for (String value : Arrays.asList("yes", "y", "true", "t", "yEs", "trUe")) {
632             event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, value));
633             assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem));
634         }
635
636         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "false"));
637         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
638
639         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "no"));
640         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
641     }
642
643     @Test
644     public void testRequestLock() {
645         final CompletableFuture<OperationOutcome> future1 = mgr.requestLock(LOCK1, callback1);
646         final CompletableFuture<OperationOutcome> future2 = mgr.requestLock(LOCK2, callback2);
647         assertSame(future1, mgr.requestLock(LOCK1, callback3));
648
649         assertEquals(2, locks.size());
650
651         assertTrue(future1.isDone());
652         assertTrue(future2.isDone());
653
654         verify(callback1, never()).accept(any());
655         verify(callback2, never()).accept(any());
656         verify(callback3, never()).accept(any());
657
658         // indicate that the first lock failed
659         locks.get(0).notifyUnavailable();
660
661         verify(callback1).accept(any());
662         verify(callback2, never()).accept(any());
663         verify(callback3).accept(any());
664     }
665
666     @Test
667     public void testMakeOperationManager() throws ControlLoopException {
668         // use a manager that creates real operation managers
669         mgr = new MyManager(params, event, workMem);
670
671         assertThatCode(() -> mgr.start()).doesNotThrowAnyException();
672     }
673
674     @Test
675     public void testGetBlockingExecutor() throws Exception {
676         mgr = new ControlLoopEventManager2(params, event, workMem);
677         assertThatCode(() -> mgr.getBlockingExecutor()).doesNotThrowAnyException();
678     }
679
680     @Test
681     public void testToString() {
682         assertNotNull(mgr.toString());
683     }
684
685
686     private void nextStep(ControlLoopOperationManager2 oper, boolean moreSteps, PolicyResult result) {
687         when(oper.nextStep()).thenReturn(moreSteps);
688         when(oper.getOperationResult()).thenReturn(result);
689
690         if (result == PolicyResult.SUCCESS) {
691             when(oper.getState()).thenReturn(State.OPERATION_SUCCESS);
692         } else {
693             when(oper.getState()).thenReturn(State.OPERATION_FAILURE);
694         }
695
696         mgr.updated(oper);
697
698         updateCount++;
699
700         verify(workMem, times(updateCount)).update(factHandle, mgr);
701     }
702
703     private void runRule() {
704         assertTrue(mgr.isActive());
705         mgr.nextStep();
706     }
707
708     private void runOperation(PolicyResult finalResult) throws ControlLoopException {
709         mgr.start();
710         verify(oper1).start(anyLong());
711
712         assertTrue(mgr.isActive());
713
714         nextStep(oper1, true, PolicyResult.SUCCESS);
715         runRule();
716
717         nextStep(oper1, false, finalResult);
718         runRule();
719
720         assertFalse(mgr.isActive());
721
722         // should have no effect, because it's done
723         mgr.updated(oper1);
724         verify(workMem, times(updateCount)).update(any(), any());
725     }
726
727     private void verifyNotification(ControlLoopNotificationType expectedType, String expectedMsg) {
728         VirtualControlLoopNotification notif = mgr.getNotification();
729         assertEquals(expectedType, notif.getNotification());
730         assertEquals(expectedMsg, notif.getMessage());
731     }
732
733     private List<ControlLoopOperation> makeHistory(String message) {
734         ControlLoopOperation clo = new ControlLoopOperation();
735         clo.setMessage("history-" + message);
736
737         return List.of(clo);
738     }
739
740     private void loadPolicy(String fileName) throws CoderException {
741         ToscaServiceTemplate template =
742                         yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
743         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
744
745         params.setToscaPolicy(tosca);
746     }
747
748     private void freeLocks() {
749         ArgumentCaptor<Runnable> runCaptor = ArgumentCaptor.forClass(Runnable.class);
750         verify(executor).execute(runCaptor.capture());
751
752         runCaptor.getValue().run();
753     }
754
755
756     private class MyManager extends ControlLoopEventManager2 {
757         private static final long serialVersionUID = 1L;
758
759         public MyManager(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
760                         throws ControlLoopException {
761
762             super(params, event, workMem);
763         }
764
765         @Override
766         protected ExecutorService getBlockingExecutor() {
767             return executor;
768         }
769
770         @Override
771         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
772             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
773             locks.add(lock);
774             callback.lockAvailable(lock);
775         }
776
777         @Override
778         public ActorService getActorService() {
779             return actors;
780         }
781
782         @Override
783         public OperationHistoryDataManager getDataManager() {
784             return dataMgr;
785         }
786     }
787
788
789     private class MyManagerWithOper extends MyManager {
790         private static final long serialVersionUID = 1L;
791
792         public MyManagerWithOper(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
793                         throws ControlLoopException {
794
795             super(params, event, workMem);
796         }
797
798         @Override
799         protected ControlLoopOperationManager2 makeOperationManager(ControlLoopEventContext ctx, Policy policy) {
800             switch (policy.getActor()) {
801                 case "First":
802                     return oper1;
803                 case "Second":
804                     return oper2;
805                 case "Third":
806                     return oper3;
807                 default:
808                     throw new IllegalArgumentException("unknown policy actor " + policy.getActor());
809             }
810         }
811     }
812 }