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