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