2  * ============LICENSE_START=======================================================
 
   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
 
  11  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  21 package org.onap.policy.controlloop.eventmanager;
 
  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;
 
  38 import java.util.ArrayList;
 
  39 import java.util.Arrays;
 
  40 import java.util.List;
 
  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.OperationResult;
 
  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.drools.core.lock.LockCallback;
 
  75 import org.onap.policy.drools.core.lock.LockImpl;
 
  76 import org.onap.policy.drools.core.lock.LockState;
 
  77 import org.onap.policy.drools.domain.models.operational.Operation;
 
  78 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 
  79 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 
  81 public class ControlLoopEventManager2Test {
 
  82     private static final UUID REQ_ID = UUID.randomUUID();
 
  83     private static final String CL_NAME = "my-closed-loop-name";
 
  84     private static final String POLICY_NAME = "my-policy-name";
 
  85     private static final String POLICY_SCOPE = "my-scope";
 
  86     private static final String POLICY_VERSION = "1.2.3";
 
  87     private static final String MY_TARGET = "my-target";
 
  88     private static final String LOCK1 = "my-lock-A";
 
  89     private static final String LOCK2 = "my-lock-B";
 
  90     private static final Coder yamlCoder = new StandardYamlCoder();
 
  93     private WorkingMemory workMem;
 
  95     private Consumer<OperationOutcome> callback1;
 
  97     private Consumer<OperationOutcome> callback2;
 
  99     private Consumer<OperationOutcome> callback3;
 
 101     private FactHandle factHandle;
 
 103     private ActorService actors;
 
 105     private OperationHistoryDataManager dataMgr;
 
 107     private ControlLoopOperationManager2 oper1;
 
 109     private ControlLoopOperationManager2 oper2;
 
 111     private ControlLoopOperationManager2 oper3;
 
 113     private ExecutorService executor;
 
 115     private long preCreateTimeMs;
 
 116     private List<LockImpl> locks;
 
 117     private ToscaPolicy tosca;
 
 118     private ControlLoopParams params;
 
 119     private VirtualControlLoopEvent event;
 
 120     private int updateCount;
 
 121     private ControlLoopEventManager2 mgr;
 
 127     public void setUp() throws ControlLoopException, CoderException {
 
 128         MockitoAnnotations.initMocks(this);
 
 130         when(oper1.getHistory()).thenReturn(makeHistory("A"));
 
 131         when(oper2.getHistory()).thenReturn(makeHistory("B"));
 
 132         when(oper3.getHistory()).thenReturn(makeHistory("C"));
 
 134         when(oper1.getActor()).thenReturn("First");
 
 135         when(oper1.getOperation()).thenReturn("OperationA");
 
 136         when(oper1.getOperationMessage()).thenReturn("message-A");
 
 137         when(oper1.getOperationHistory()).thenReturn("history-A");
 
 139         when(oper2.getActor()).thenReturn("Second");
 
 140         when(oper2.getOperation()).thenReturn("OperationB");
 
 141         when(oper2.getOperationMessage()).thenReturn("message-B");
 
 142         when(oper2.getOperationHistory()).thenReturn("history-B");
 
 144         when(oper3.getActor()).thenReturn("Third");
 
 145         when(oper3.getOperation()).thenReturn("OperationC");
 
 146         when(oper3.getOperationMessage()).thenReturn("message-C");
 
 147         when(oper3.getOperationHistory()).thenReturn("history-C");
 
 149         when(workMem.getFactHandle(any())).thenReturn(factHandle);
 
 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(ControlLoopTargetType.VNF);
 
 159         params = new ControlLoopParams();
 
 160         params.setClosedLoopControlName(CL_NAME);
 
 161         params.setPolicyName(POLICY_NAME);
 
 162         params.setPolicyScope(POLICY_SCOPE);
 
 163         params.setPolicyVersion(POLICY_VERSION);
 
 165         loadPolicy("eventManager/event-mgr-simple.yaml");
 
 167         locks = new ArrayList<>();
 
 171         preCreateTimeMs = System.currentTimeMillis();
 
 173         mgr = new MyManagerWithOper(params, event, workMem);
 
 177     public void testConstructor() {
 
 178         assertEquals(POLICY_NAME, mgr.getPolicyName());
 
 180         Map<String, String> orig = event.getAai();
 
 182         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
 
 183         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 184                         .hasMessage("is-closed-loop-disabled is set to true on VServer or VNF");
 
 186         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "inactive"));
 
 187         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 188                         .hasMessage("prov-status is not ACTIVE on VServer or VNF");
 
 192         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 195         event.setTarget("unknown-target");
 
 196         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 197                         .isInstanceOf(ControlLoopException.class);
 
 201      * Runs through a policy that has several operations.
 
 204     public void testMultiOperation() throws Exception {
 
 206         loadPolicy("eventManager/event-mgr-multi.yaml");
 
 208         mgr = new MyManagerWithOper(params, event, workMem);
 
 211         for (ControlLoopOperationManager2 oper : Arrays.asList(oper1, oper2, oper3)) {
 
 212             assertTrue(mgr.isActive());
 
 213             nextStep(oper, true, OperationResult.SUCCESS);
 
 216             assertTrue(mgr.isActive());
 
 217             nextStep(oper, false, OperationResult.SUCCESS);
 
 221         assertFalse(mgr.isActive());
 
 225     public void testStart() throws Exception {
 
 230         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
 
 231                         .hasMessage("manager already started");
 
 235      * Tests start() error cases.
 
 238     public void testStartErrors() throws Exception {
 
 240         ControlLoopEventManager2 mgr2 = new ControlLoopEventManager2(params, event, workMem);
 
 241         ControlLoopEventManager2 mgr3 = Serializer.roundTrip(mgr2);
 
 242         assertThatCode(() -> mgr3.start()).isInstanceOf(IllegalStateException.class)
 
 243                         .hasMessage("manager is no longer active");
 
 246         when(workMem.getFactHandle(any())).thenReturn(null);
 
 247         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
 
 248                         .hasMessage("manager is not in working memory");
 
 252     public void testNextStep_testStartOperationSuccess() throws ControlLoopException {
 
 253         runOperation(OperationResult.SUCCESS);
 
 255         VirtualControlLoopNotification notif = mgr.getNotification();
 
 256         assertEquals(ControlLoopNotificationType.FINAL_SUCCESS, notif.getNotification());
 
 257         assertNull(notif.getMessage());
 
 259         assertThatCode(() -> mgr.nextStep()).doesNotThrowAnyException();
 
 263      * Tests nextStep() when the next step is invalid, which should cause an exception to
 
 264      * be thrown by the processor.
 
 267     public void testNextStepMissing() throws Exception {
 
 270         when(oper1.nextStep()).thenThrow(new IllegalArgumentException("expected exception"));
 
 274         assertFalse(mgr.isActive());
 
 276         VirtualControlLoopNotification notif = mgr.getNotification();
 
 277         assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notif.getNotification());
 
 278         assertEquals("Policy processing aborted due to policy error", notif.getMessage());
 
 279         assertTrue(notif.getHistory().isEmpty());
 
 283      * Tests startOperation() with FINAL_FAILURE_EXCEPTION.
 
 286     public void testStartOperationException() throws ControlLoopException {
 
 287         runOperation(OperationResult.FAILURE_EXCEPTION);
 
 289         VirtualControlLoopNotification notif = mgr.getNotification();
 
 290         assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notif.getNotification());
 
 291         assertEquals("Exception in processing closed loop", notif.getMessage());
 
 295      * Tests startOperation() with FINAL_FAILURE.
 
 298     public void testStartOperationFailure() throws ControlLoopException {
 
 299         runOperation(OperationResult.FAILURE);
 
 301         VirtualControlLoopNotification notif = mgr.getNotification();
 
 302         assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notif.getNotification());
 
 303         assertNull(notif.getMessage());
 
 307      * Tests startOperation() with FINAL_OPENLOOP.
 
 310     public void testStartOperationOpenLoop() throws ControlLoopException {
 
 311         runOperation(OperationResult.FAILURE_GUARD);
 
 313         VirtualControlLoopNotification notif = mgr.getNotification();
 
 314         assertEquals(ControlLoopNotificationType.FINAL_OPENLOOP, notif.getNotification());
 
 315         assertNull(notif.getMessage());
 
 319     public void testIsActive() throws Exception {
 
 320         mgr = new ControlLoopEventManager2(params, event, workMem);
 
 321         assertTrue(mgr.isActive());
 
 323         ControlLoopEventManager2 mgr2 = Serializer.roundTrip(mgr);
 
 324         assertFalse(mgr2.isActive());
 
 328     public void testUpdated() throws ControlLoopException {
 
 331         // not the active operation - should be ignored
 
 333         verify(workMem, never()).update(any(), any());
 
 335         VirtualControlLoopNotification notif;
 
 337         // check notification data
 
 338         when(oper1.getState()).thenReturn(State.LOCK_DENIED);
 
 340         notif = mgr.getNotification();
 
 341         assertNotNull(notif.getHistory());
 
 344          * try the various cases
 
 346         when(oper1.getState()).thenReturn(State.LOCK_DENIED);
 
 348         verifyNotification(ControlLoopNotificationType.REJECTED, "The target my-target is already locked");
 
 350         when(oper1.getState()).thenReturn(State.LOCK_LOST);
 
 352         verifyNotification(ControlLoopNotificationType.OPERATION_FAILURE, "The target my-target is no longer locked");
 
 354         when(oper1.getState()).thenReturn(State.GUARD_STARTED);
 
 356         verifyNotification(ControlLoopNotificationType.OPERATION, "Sending guard query for First OperationA");
 
 358         when(oper1.getState()).thenReturn(State.GUARD_PERMITTED);
 
 360         verifyNotification(ControlLoopNotificationType.OPERATION, "Guard result for First OperationA is Permit");
 
 362         when(oper1.getState()).thenReturn(State.GUARD_DENIED);
 
 364         verifyNotification(ControlLoopNotificationType.OPERATION, "Guard result for First OperationA is Deny");
 
 366         when(oper1.getState()).thenReturn(State.OPERATION_STARTED);
 
 368         verifyNotification(ControlLoopNotificationType.OPERATION, "message-A");
 
 370         when(oper1.getState()).thenReturn(State.OPERATION_SUCCESS);
 
 372         verifyNotification(ControlLoopNotificationType.OPERATION_SUCCESS, "history-A");
 
 374         when(oper1.getState()).thenReturn(State.OPERATION_FAILURE);
 
 376         verifyNotification(ControlLoopNotificationType.OPERATION_FAILURE, "history-A");
 
 378         // should still be active
 
 379         assertTrue(mgr.isActive());
 
 384         when(oper1.getState()).thenReturn(State.CONTROL_LOOP_TIMEOUT);
 
 386         verifyNotification(ControlLoopNotificationType.FINAL_FAILURE, "Control Loop timed out");
 
 388         // should now be done
 
 389         assertFalse(mgr.isActive());
 
 393     public void testDestroy() {
 
 394         mgr.requestLock(LOCK1, callback1);
 
 395         mgr.requestLock(LOCK2, callback2);
 
 396         mgr.requestLock(LOCK1, callback3);
 
 402         for (LockImpl lock : locks) {
 
 403             assertTrue(lock.isUnavailable());
 
 408      * Tests destroy() once it has been started.
 
 411     public void testDestroyStarted() throws ControlLoopException {
 
 414         mgr.requestLock(LOCK1, callback1);
 
 415         mgr.requestLock(LOCK2, callback2);
 
 416         mgr.requestLock(LOCK1, callback3);
 
 422         // should have canceled the operation
 
 423         verify(oper1).cancel();
 
 425         for (LockImpl lock : locks) {
 
 426             assertTrue(lock.isUnavailable());
 
 431     public void testMakeNotification() throws ControlLoopException {
 
 433         assertNotNull(mgr.makeNotification());
 
 437         nextStep(oper1, true, OperationResult.SUCCESS);
 
 440         // check notification while running
 
 441         VirtualControlLoopNotification notif = mgr.getNotification();
 
 442         assertEquals("history-A", notif.getMessage());
 
 444         List<ControlLoopOperation> history = notif.getHistory();
 
 445         assertNotNull(history);
 
 447         nextStep(oper1, false, OperationResult.SUCCESS);
 
 450         assertFalse(mgr.isActive());
 
 452         // check notification when complete
 
 453         notif = mgr.getNotification();
 
 454         assertNull(notif.getMessage());
 
 455         assertEquals(history, notif.getHistory());
 
 459     public void testOnNewEvent() {
 
 460         VirtualControlLoopEvent event2 = new VirtualControlLoopEvent(event);
 
 461         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
 
 463         event2.setPayload("other payload");
 
 464         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
 
 465         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
 
 466         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event));
 
 468         event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
 
 469         assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
 
 471         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
 
 472         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
 
 474         event2.setClosedLoopEventStatus(null);
 
 475         assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
 
 479     public void testDetmControlLoopTimeoutMs() throws Exception {
 
 480         verifyTimeout(1200 * 1000L);
 
 483     private void verifyTimeout(long timeMs) {
 
 484         long end = mgr.getEndTimeMs();
 
 485         assertTrue(end >= preCreateTimeMs + timeMs);
 
 486         assertTrue(end < preCreateTimeMs + timeMs + 5000);
 
 490     public void testCheckEventSyntax() {
 
 491         // initially, it's valid
 
 492         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 494         event.setTarget("unknown-target");
 
 495         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 496                         .hasMessage("target field invalid");
 
 498         event.setTarget(null);
 
 499         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 500                         .hasMessage("No target field");
 
 502         // abated supersedes previous errors - so it shouldn't throw an exception
 
 503         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
 
 504         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 506         event.setRequestId(null);
 
 507         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 508                         .hasMessage("No request ID");
 
 510         event.setClosedLoopControlName(null);
 
 511         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 512                         .hasMessage("No control loop name");
 
 516     public void testValidateStatus() {
 
 517         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
 
 518         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 520         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
 
 521         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 523         event.setClosedLoopEventStatus(null);
 
 524         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 525                         .hasMessage("Invalid value in closedLoopEventStatus");
 
 529     public void testValidateAaiData() {
 
 530         event.setTargetType("unknown-target-type");
 
 531         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 532                         .hasMessage("The target type is not supported");
 
 534         event.setTargetType(null);
 
 535         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 536                         .hasMessage("The Target type is null");
 
 539         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 540                         .hasMessage("AAI is null");
 
 543         event.setTargetType(ControlLoopTargetType.VM);
 
 544         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
 
 545         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 547         event.setAai(Map.of());
 
 548         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
 
 551         event.setTargetType(ControlLoopTargetType.VNF);
 
 552         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
 
 553         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 555         event.setAai(Map.of());
 
 556         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
 
 559         event.setTargetType(ControlLoopTargetType.PNF);
 
 560         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
 
 561         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 563         event.setAai(Map.of());
 
 564         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
 
 568     public void testValidateAaiVmVnfData() {
 
 569         event.setTargetType(ControlLoopTargetType.VM);
 
 570         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
 
 571         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 573         event.setAai(Map.of(ControlLoopEventManager2.VSERVER_VSERVER_NAME, MY_TARGET));
 
 574         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 576         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_NAME, MY_TARGET));
 
 577         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 579         event.setAai(Map.of());
 
 580         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class).hasMessage(
 
 581                         "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
 
 585     public void testValidateAaiPnfData() {
 
 586         event.setTargetType(ControlLoopTargetType.PNF);
 
 587         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
 
 588         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 590         event.setAai(Map.of());
 
 591         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 592                         .hasMessage("AAI PNF object key pnf-name is missing");
 
 596     public void testIsClosedLoopDisabled() {
 
 597         Map<String, String> orig = event.getAai();
 
 599         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
 
 600         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 601                         .isInstanceOf(IllegalStateException.class);
 
 603         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "true"));
 
 604         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 605                         .isInstanceOf(IllegalStateException.class);
 
 607         event.setAai(addAai(orig, ControlLoopEventManager2.PNF_IS_IN_MAINT, "true"));
 
 608         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 609                         .isInstanceOf(IllegalStateException.class);
 
 612     private Map<String, String> addAai(Map<String, String> original, String key, String value) {
 
 613         Map<String, String> map = new TreeMap<>(original);
 
 619     public void testIsProvStatusInactive() {
 
 620         Map<String, String> orig = event.getAai();
 
 622         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "ACTIVE"));
 
 623         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 625         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "inactive"));
 
 626         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 627                         .isInstanceOf(IllegalStateException.class);
 
 629         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "ACTIVE"));
 
 630         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 632         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "inactive"));
 
 633         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 634                         .isInstanceOf(IllegalStateException.class);
 
 638     public void testIsAaiTrue() {
 
 639         Map<String, String> orig = event.getAai();
 
 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                             .isInstanceOf(IllegalStateException.class);
 
 647         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "false"));
 
 648         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 650         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "no"));
 
 651         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 655     public void testRequestLock() {
 
 656         final CompletableFuture<OperationOutcome> future1 = mgr.requestLock(LOCK1, callback1);
 
 657         final CompletableFuture<OperationOutcome> future2 = mgr.requestLock(LOCK2, callback2);
 
 658         assertSame(future1, mgr.requestLock(LOCK1, callback3));
 
 660         assertEquals(2, locks.size());
 
 662         assertTrue(future1.isDone());
 
 663         assertTrue(future2.isDone());
 
 665         verify(callback1, never()).accept(any());
 
 666         verify(callback2, never()).accept(any());
 
 667         verify(callback3, never()).accept(any());
 
 669         // indicate that the first lock failed
 
 670         locks.get(0).notifyUnavailable();
 
 672         verify(callback1).accept(any());
 
 673         verify(callback2, never()).accept(any());
 
 674         verify(callback3).accept(any());
 
 678     public void testMakeOperationManager() throws ControlLoopException {
 
 679         // use a manager that creates real operation managers
 
 680         mgr = new MyManager(params, event, workMem);
 
 682         assertThatCode(() -> mgr.start()).doesNotThrowAnyException();
 
 686     public void testGetBlockingExecutor() throws Exception {
 
 687         mgr = new ControlLoopEventManager2(params, event, workMem);
 
 688         assertThatCode(() -> mgr.getBlockingExecutor()).doesNotThrowAnyException();
 
 692     public void testToString() {
 
 693         assertNotNull(mgr.toString());
 
 697     private void nextStep(ControlLoopOperationManager2 oper, boolean moreSteps, OperationResult result) {
 
 698         when(oper.nextStep()).thenReturn(moreSteps);
 
 699         when(oper.getOperationResult()).thenReturn(result);
 
 701         if (result == OperationResult.SUCCESS) {
 
 702             when(oper.getState()).thenReturn(State.OPERATION_SUCCESS);
 
 704             when(oper.getState()).thenReturn(State.OPERATION_FAILURE);
 
 711         verify(workMem, times(updateCount)).update(factHandle, mgr);
 
 714     private void runRule() {
 
 715         assertTrue(mgr.isActive());
 
 719     private void runOperation(OperationResult finalResult) throws ControlLoopException {
 
 721         verify(oper1).start(anyLong());
 
 723         assertTrue(mgr.isActive());
 
 725         nextStep(oper1, true, OperationResult.SUCCESS);
 
 728         nextStep(oper1, false, finalResult);
 
 731         assertFalse(mgr.isActive());
 
 733         // should have no effect, because it's done
 
 735         verify(workMem, times(updateCount)).update(any(), any());
 
 738     private void verifyNotification(ControlLoopNotificationType expectedType, String expectedMsg) {
 
 739         VirtualControlLoopNotification notif = mgr.getNotification();
 
 740         assertEquals(expectedType, notif.getNotification());
 
 741         assertEquals(expectedMsg, notif.getMessage());
 
 744     private List<ControlLoopOperation> makeHistory(String message) {
 
 745         ControlLoopOperation clo = new ControlLoopOperation();
 
 746         clo.setMessage("history-" + message);
 
 751     private void loadPolicy(String fileName) throws CoderException {
 
 752         ToscaServiceTemplate template =
 
 753                         yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
 
 754         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
 
 756         params.setToscaPolicy(tosca);
 
 759     private void freeLocks() {
 
 760         ArgumentCaptor<Runnable> runCaptor = ArgumentCaptor.forClass(Runnable.class);
 
 761         verify(executor).execute(runCaptor.capture());
 
 763         runCaptor.getValue().run();
 
 767     private class MyManager extends ControlLoopEventManager2 {
 
 768         private static final long serialVersionUID = 1L;
 
 770         public MyManager(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
 
 771                         throws ControlLoopException {
 
 773             super(params, event, workMem);
 
 777         protected ExecutorService getBlockingExecutor() {
 
 782         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
 
 783             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
 
 785             callback.lockAvailable(lock);
 
 789         public ActorService getActorService() {
 
 794         public OperationHistoryDataManager getDataManager() {
 
 800     private class MyManagerWithOper extends MyManager {
 
 801         private static final long serialVersionUID = 1L;
 
 803         public MyManagerWithOper(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
 
 804                         throws ControlLoopException {
 
 806             super(params, event, workMem);
 
 810         protected ControlLoopOperationManager2 makeOperationManager(ControlLoopEventContext ctx, Operation policy) {
 
 811             switch (policy.getActorOperation().getActor()) {
 
 819                     throw new IllegalArgumentException("unknown policy actor " + policy.getActorOperation().getActor());