d4979fb796b6ee3c0e5786039dab7d05e49e0e86
[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.drools.apps.controller.usecases;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
26 import static org.assertj.core.api.Assertions.assertThatThrownBy;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertNull;
31 import static org.junit.Assert.assertSame;
32 import static org.junit.Assert.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.anyLong;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38
39 import java.time.Instant;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.Deque;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.TreeMap;
47 import java.util.UUID;
48 import java.util.concurrent.ExecutorService;
49 import java.util.concurrent.ForkJoinPool;
50 import org.drools.core.WorkingMemory;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.kie.api.runtime.rule.FactHandle;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 import org.onap.policy.common.utils.coder.Coder;
57 import org.onap.policy.common.utils.coder.CoderException;
58 import org.onap.policy.common.utils.coder.StandardYamlCoder;
59 import org.onap.policy.common.utils.io.Serializer;
60 import org.onap.policy.common.utils.resources.ResourceUtils;
61 import org.onap.policy.controlloop.ControlLoopEventStatus;
62 import org.onap.policy.controlloop.ControlLoopException;
63 import org.onap.policy.controlloop.ControlLoopResponse;
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.Operation;
69 import org.onap.policy.controlloop.actorserviceprovider.OperationFinalResult;
70 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
71 import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
72 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
73 import org.onap.policy.controlloop.actorserviceprovider.Operator;
74 import org.onap.policy.controlloop.actorserviceprovider.TargetType;
75 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
76 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
77 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
78 import org.onap.policy.controlloop.eventmanager.ActorConstants;
79 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
80 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2Drools;
81 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
82 import org.onap.policy.drools.apps.controller.usecases.UsecasesEventManager.NewEventStatus;
83 import org.onap.policy.drools.apps.controller.usecases.step.AaiCqStep2;
84 import org.onap.policy.drools.apps.controller.usecases.step.AaiGetPnfStep2;
85 import org.onap.policy.drools.apps.controller.usecases.step.AaiGetTenantStep2;
86 import org.onap.policy.drools.apps.controller.usecases.step.GetTargetEntityStep2;
87 import org.onap.policy.drools.apps.controller.usecases.step.GuardStep2;
88 import org.onap.policy.drools.apps.controller.usecases.step.Step2;
89 import org.onap.policy.drools.core.lock.LockCallback;
90 import org.onap.policy.drools.core.lock.LockImpl;
91 import org.onap.policy.drools.core.lock.LockState;
92 import org.onap.policy.drools.system.PolicyEngine;
93 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
94 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
95 import org.onap.policy.sdnr.PciBody;
96 import org.onap.policy.sdnr.PciMessage;
97 import org.onap.policy.sdnr.PciResponse;
98
99 public class UsecasesEventManagerTest {
100     private static final UUID REQ_ID = UUID.randomUUID();
101     private static final String CL_NAME = "my-closed-loop-name";
102     private static final String POLICY_NAME = "my-policy-name";
103     private static final String POLICY_SCOPE = "my-scope";
104     private static final String POLICY_VERSION = "1.2.3";
105     private static final String SIMPLE_ACTOR = "First";
106     private static final String SIMPLE_OPERATION = "OperationA";
107     private static final String MY_TARGET = "my-target";
108     private static final String EVENT_MGR_MULTI_YAML =
109                     "../eventmanager/src/test/resources/eventManager/event-mgr-multi.yaml";
110     private static final String EVENT_MGR_SIMPLE_YAML =
111                     "../eventmanager/src/test/resources/eventManager/event-mgr-simple.yaml";
112     private static final Coder yamlCoder = new StandardYamlCoder();
113     private static final String OUTCOME_MSG = "my outcome message";
114     private static final String MY_SINK = "my-topic-sink";
115
116     @Mock
117     private PolicyEngine engineMgr;
118     @Mock
119     private WorkingMemory workMem;
120     @Mock
121     private FactHandle factHandle;
122     @Mock
123     private Operator policyOperator;
124     @Mock
125     private Operation policyOperation;
126     @Mock
127     private Actor policyActor;
128     @Mock
129     private ActorService actors;
130     @Mock
131     private OperationHistoryDataManager dataMgr;
132     @Mock
133     private ExecutorService executor;
134     @Mock
135     private Step2 stepa;
136     @Mock
137     private Step2 stepb;
138
139     private List<LockImpl> locks;
140     private ToscaPolicy tosca;
141     private ControlLoopParams params;
142     private VirtualControlLoopEvent event;
143     private UsecasesEventManager mgr;
144
145     /**
146      * Sets up.
147      */
148     @Before
149     public void setUp() throws ControlLoopException, CoderException {
150         MockitoAnnotations.initMocks(this);
151
152         when(actors.getActor(SIMPLE_ACTOR)).thenReturn(policyActor);
153         when(policyActor.getOperator(SIMPLE_OPERATION)).thenReturn(policyOperator);
154         when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
155         when(policyOperation.getPropertyNames()).thenReturn(Collections.emptyList());
156
157         when(workMem.getFactHandle(any())).thenReturn(factHandle);
158
159         event = new VirtualControlLoopEvent();
160         event.setRequestId(REQ_ID);
161         event.setTarget(UsecasesConstants.VSERVER_VSERVER_NAME);
162         event.setAai(new TreeMap<>(Map.of(UsecasesConstants.VSERVER_VSERVER_NAME, MY_TARGET)));
163         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
164         event.setClosedLoopControlName(CL_NAME);
165         event.setTargetType(ControlLoopTargetType.VNF);
166
167         params = new ControlLoopParams();
168         params.setClosedLoopControlName(CL_NAME);
169         params.setPolicyName(POLICY_NAME);
170         params.setPolicyScope(POLICY_SCOPE);
171         params.setPolicyVersion(POLICY_VERSION);
172
173         loadPolicy(EVENT_MGR_SIMPLE_YAML);
174
175         locks = new ArrayList<>();
176
177         mgr = new MyManager(params, event, workMem);
178     }
179
180     @Test
181     public void testConstructor() {
182         assertEquals(POLICY_NAME, mgr.getPolicyName());
183         assertSame(event, mgr.getEvent());
184
185         Map<String, String> orig = event.getAai();
186
187         event.setAai(addAai(orig, UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
188         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
189                         .hasMessage("is-closed-loop-disabled is set to true on VServer or VNF");
190
191         // vserver ACTIVE
192         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS,
193                         UsecasesConstants.PROV_STATUS_ACTIVE.toUpperCase()));
194         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
195
196         // vserver active
197         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS,
198                         UsecasesConstants.PROV_STATUS_ACTIVE.toLowerCase()));
199         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
200
201         // vserver inactive
202         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS, "inactive"));
203         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
204                         .hasMessage("prov-status is not ACTIVE on VServer or VNF");
205
206         // vnf ACTIVE
207         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS,
208                         UsecasesConstants.PROV_STATUS_ACTIVE.toUpperCase()));
209         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
210
211         // vnf active
212         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS,
213                         UsecasesConstants.PROV_STATUS_ACTIVE.toLowerCase()));
214         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
215
216         // vnf inactive
217         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS, "inactive"));
218         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
219                         .hasMessage("prov-status is not ACTIVE on VServer or VNF");
220
221         // valid
222         event.setAai(orig);
223         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
224
225         // invalid
226         event.setTarget("unknown-target");
227         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
228                         .isInstanceOf(ControlLoopException.class);
229     }
230
231     @Test
232     public void testIsActive() throws Exception {
233         mgr = new UsecasesEventManager(params, event, workMem);
234         assertTrue(mgr.isActive());
235
236         // deserialized manager should be inactive
237         UsecasesEventManager mgr2 = Serializer.roundTrip(mgr);
238         assertFalse(mgr2.isActive());
239     }
240
241     @Test
242     public void testDestroy_testGetSteps() {
243         // add some steps to the queue
244         mgr.getSteps().add(stepa);
245         mgr.getSteps().add(stepb);
246
247         mgr.destroy();
248
249         verify(stepa).cancel();
250         verify(stepb).cancel();
251
252         // if superclass destroy() was invoked, then freeLock() should have been submitted
253         // to the executor
254         verify(executor).execute(any());
255     }
256
257     @Test
258     public void testOnStart() throws ControlLoopException {
259         OperationOutcome outcome = makeOutcome();
260
261         mgr.start();
262         mgr.onStart(outcome);
263
264         assertSame(outcome, mgr.getOutcomes().poll());
265         assertThat(mgr.getOutcomes()).isEmpty();
266
267         verify(workMem).update(factHandle, mgr);
268     }
269
270     @Test
271     public void testOnComplete() throws ControlLoopException {
272         OperationOutcome outcome = makeCompletedOutcome();
273
274         mgr.start();
275         mgr.onComplete(outcome);
276
277         assertSame(outcome, mgr.getOutcomes().poll());
278         assertThat(mgr.getOutcomes()).isEmpty();
279
280         verify(workMem).update(factHandle, mgr);
281     }
282
283     @Test
284     public void testToString() {
285         assertNotNull(mgr.toString());
286     }
287
288     @Test
289     public void testStart() throws ControlLoopException {
290         // start it
291         mgr.start();
292
293         // cannot re-start
294         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
295                         .hasMessage("manager already started");
296     }
297
298     /**
299      * Tests start() when the manager is not in working memory.
300      */
301     @Test
302     public void testStartNotInWorkingMemory() throws ControlLoopException {
303         when(workMem.getFactHandle(any())).thenReturn(null);
304
305         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
306                         .hasMessage("manager is not in working memory");
307     }
308
309     /**
310      * Tests start() when the manager is not active.
311      */
312     @Test
313     public void testStartInactive() throws Exception {
314         // make an inactive manager by deserializing it
315         mgr = Serializer.roundTrip(new UsecasesEventManager(params, event, workMem));
316
317         // cannot re-start
318         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
319                         .hasMessage("manager is no longer active");
320     }
321
322     @Test
323     public void testAbort() {
324         mgr.abort(UsecasesEventManager.State.DONE, OperationFinalResult.FINAL_FAILURE_GUARD, "some message");
325
326         assertEquals(UsecasesEventManager.State.DONE, mgr.getState());
327         assertEquals(OperationFinalResult.FINAL_FAILURE_GUARD, mgr.getFinalResult());
328         assertEquals("some message", mgr.getFinalMessage());
329
330         // try null state
331         assertThatThrownBy(() -> mgr.abort(null, OperationFinalResult.FINAL_FAILURE_GUARD, ""))
332                         .isInstanceOf(NullPointerException.class).hasMessageContaining("finalState");
333     }
334
335     @Test
336     public void testLoadNextPolicy_testGetFullHistory_testGetPartialHistory() throws Exception {
337         loadPolicy(EVENT_MGR_MULTI_YAML);
338         mgr = new MyManager(params, event, workMem);
339
340         // start and load step for first policy
341         mgr.start();
342         assertEquals("OperationA", mgr.getSteps().poll().getOperationName());
343         assertNull(mgr.getFinalResult());
344
345         // add an outcome
346         OperationOutcome outcome = makeOutcome();
347         mgr.addToHistory(outcome);
348
349         // indicate success and load next policy
350         mgr.loadNextPolicy(OperationResult.SUCCESS);
351         assertEquals("OperationB", mgr.getSteps().poll().getOperationName());
352         assertNull(mgr.getFinalResult());
353
354         // loadPolicy() should clear the partial history, but not the full history
355         assertThat(mgr.getPartialHistory()).isEmpty();
356         assertThat(mgr.getFullHistory()).hasSize(1);
357
358         // indicate failure - should go to final failure
359         mgr.loadNextPolicy(OperationResult.FAILURE);
360         assertEquals(OperationFinalResult.FINAL_FAILURE, mgr.getFinalResult());
361     }
362
363     @Test
364     public void testLoadPolicy() throws ControlLoopException {
365         // start() will invoke loadPolicy()
366         mgr.start();
367
368         assertNull(mgr.getFinalResult());
369
370         Step2 step = mgr.getSteps().peek();
371         assertNotNull(step);
372         assertEquals("First", step.getActorName());
373         assertEquals("OperationA", step.getOperationName());
374
375         ControlLoopOperationParams params2 = step.getParams();
376         assertSame(actors, params2.getActorService());
377         assertSame(REQ_ID, params2.getRequestId());
378         assertSame(ForkJoinPool.commonPool(), params2.getExecutor());
379         assertNotNull(params2.getTargetType());
380         assertNotNull(params2.getTargetEntityIds());
381         assertEquals(Integer.valueOf(300), params2.getTimeoutSec());
382         assertEquals(Integer.valueOf(0), params2.getRetry());
383         assertThat(params2.getPayload()).isEmpty();
384         assertNotNull(params2.getStartCallback());
385         assertNotNull(params2.getCompleteCallback());
386     }
387
388     @Test
389     public void testLoadPreprocessorSteps() {
390         stepa = new Step2(mgr, ControlLoopOperationParams.builder().build(), event) {
391             @Override
392             public List<String> getPropertyNames() {
393                 return List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION);
394             }
395
396             @Override
397             protected Operation buildOperation() {
398                 return policyOperation;
399             }
400         };
401
402         mgr.getSteps().add(stepa);
403         mgr.getSteps().add(stepb);
404
405         mgr.loadPreprocessorSteps();
406
407         Deque<Step2> steps = mgr.getSteps();
408
409         Step2 lockStep = steps.poll();
410         assertNotNull(lockStep);
411         assertEquals(ActorConstants.LOCK_ACTOR, lockStep.getActorName());
412         assertThat(steps.poll()).isInstanceOf(AaiCqStep2.class);
413         assertThat(steps.poll()).isInstanceOf(GuardStep2.class);
414         assertSame(stepa, steps.poll());
415         assertSame(stepb, steps.poll());
416         assertThat(steps).isEmpty();
417     }
418
419     /**
420      * Tests loadPreprocessorSteps() when there are too many steps in the queue.
421      */
422     @Test
423     public void testLoadPreprocessorStepsTooManySteps() {
424         loadStepsWithProperties(OperationProperties.AAI_PNF);
425
426         Deque<Step2> steps = mgr.getSteps();
427         stepa = steps.getFirst();
428         steps.clear();
429
430         // load up a bunch of steps
431         for (int nsteps = 0; nsteps < UsecasesEventManager.MAX_STEPS; ++nsteps) {
432             steps.add(stepa);
433         }
434
435         // should fail
436         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
437
438         // add another step, should still fail
439         steps.add(stepa);
440         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
441
442         // remove two steps - should now succeed
443         steps.remove();
444         steps.remove();
445
446         int nsteps = steps.size();
447
448         mgr.loadPreprocessorSteps();
449         assertEquals(nsteps + 1, steps.size());
450     }
451
452     /**
453      * Tests loadPreprocessorSteps() when no additional steps are needed.
454      */
455     @Test
456     public void testLoadPreprocessorStepsNothingToLoad() {
457         when(stepa.isPolicyStep()).thenReturn(false);
458         when(stepa.getPropertyNames()).thenReturn(List.of("unknown-property"));
459
460         Deque<Step2> steps = mgr.getSteps();
461         steps.add(stepa);
462         steps.add(stepb);
463
464         setTargetEntity();
465         mgr.loadPreprocessorSteps();
466
467         assertSame(stepa, steps.poll());
468         assertSame(stepb, steps.poll());
469         assertThat(steps).isEmpty();
470     }
471
472     /**
473      * Tests loadPreprocessorSteps() when an A&AI custom query is needed.
474      */
475     @Test
476     public void testLoadPreprocessorStepsCq() {
477         loadStepsWithProperties(OperationProperties.AAI_DEFAULT_CLOUD_REGION, OperationProperties.AAI_DEFAULT_TENANT);
478
479         setTargetEntity();
480         mgr.loadPreprocessorSteps();
481
482         Deque<Step2> steps = mgr.getSteps();
483
484         assertThat(steps.poll()).isInstanceOf(AaiCqStep2.class);
485         assertSame(stepa, steps.poll());
486         assertSame(stepb, steps.poll());
487         assertThat(steps).isEmpty();
488     }
489
490     /**
491      * Tests loadPreprocessorSteps() when an A&AI PNF query is needed.
492      */
493     @Test
494     public void testLoadPreprocessorStepsPnf() {
495         // doubling up the property to check both branches
496         loadStepsWithProperties(OperationProperties.AAI_PNF, OperationProperties.AAI_PNF);
497
498         setTargetEntity();
499         mgr.loadPreprocessorSteps();
500
501         Deque<Step2> steps = mgr.getSteps();
502
503         assertThat(steps.poll()).isInstanceOf(AaiGetPnfStep2.class);
504         assertSame(stepa, steps.poll());
505         assertSame(stepb, steps.poll());
506         assertThat(steps).isEmpty();
507     }
508
509     /**
510      * Tests loadPreprocessorSteps() when an A&AI Tenant query is needed.
511      */
512     @Test
513     public void testLoadPreprocessorStepsTenant() {
514         // doubling up the property to check both branches
515         event.getAai().put(Step2.VSERVER_VSERVER_NAME, "my-vserver");
516         loadStepsWithProperties(OperationProperties.AAI_VSERVER_LINK, OperationProperties.AAI_VSERVER_LINK);
517
518         setTargetEntity();
519         mgr.loadPreprocessorSteps();
520
521         Deque<Step2> steps = mgr.getSteps();
522
523         assertThat(steps.poll()).isInstanceOf(AaiGetTenantStep2.class);
524         assertSame(stepa, steps.poll());
525         assertSame(stepb, steps.poll());
526         assertThat(steps).isEmpty();
527     }
528
529     /**
530      * Tests loadPreprocessorSteps() when the target entity is unset.
531      */
532     @Test
533     public void testLoadPreprocessorStepsNeedTargetEntity() {
534         stepa = new Step2(mgr,
535                 ControlLoopOperationParams.builder()
536                         .targetType(TargetType.toTargetType(event.getTargetType()))
537                         .targetEntityIds(Map.of()).build(), event) {
538             @Override
539             public List<String> getPropertyNames() {
540                 return List.of(OperationProperties.AAI_TARGET_ENTITY);
541             }
542
543             @Override
544             protected Operation buildOperation() {
545                 return policyOperation;
546             }
547
548             @Override
549             public boolean isPolicyStep() {
550                 return false;
551             }
552         };
553
554         Deque<Step2> steps = mgr.getSteps();
555         steps.add(stepa);
556         steps.add(stepb);
557
558         mgr.loadPreprocessorSteps();
559
560         Step2 entityStep = steps.poll();
561
562         assertThat(entityStep).isInstanceOf(GetTargetEntityStep2.class);
563         assertSame(stepa, steps.poll());
564         assertSame(stepb, steps.poll());
565         assertThat(steps).isEmpty();
566
567         // put get-target-entity back onto the queue and ensure nothing else is added
568         steps.add(entityStep);
569         mgr.loadPreprocessorSteps();
570         assertSame(entityStep, steps.poll());
571         assertThat(steps).isEmpty();
572     }
573
574     @Test
575     public void testExecuteStep() {
576         mgr.bumpAttempts();
577
578         // no steps to execute
579         assertFalse(mgr.executeStep());
580         assertEquals(0, mgr.getAttempts());
581
582         // add a step to the queue
583         mgr.getSteps().add(stepa);
584
585         // step returns false
586         when(stepa.start(anyLong())).thenReturn(false);
587         assertFalse(mgr.executeStep());
588
589         // step returns true
590         when(stepa.start(anyLong())).thenReturn(true);
591         assertTrue(mgr.executeStep());
592     }
593
594     @Test
595     public void testNextStep() {
596         mgr.getSteps().add(stepa);
597
598         mgr.nextStep();
599
600         assertThat(mgr.getSteps()).isEmpty();
601     }
602
603     @Test
604     public void testBumpAttempts() {
605         assertEquals(0, mgr.getAttempts());
606
607         mgr.bumpAttempts();
608         mgr.bumpAttempts();
609         assertEquals(2, mgr.getAttempts());
610     }
611
612     @Test
613     public void testIsAbort() {
614         OperationOutcome outcome = makeCompletedOutcome();
615         outcome.setResult(OperationResult.FAILURE);
616
617         // closed loop timeout
618         outcome.setActor(ActorConstants.CL_TIMEOUT_ACTOR);
619         assertTrue(mgr.isAbort(outcome));
620
621         // lost lock
622         outcome.setActor(ActorConstants.LOCK_ACTOR);
623         assertTrue(mgr.isAbort(outcome));
624
625         // no effect for success
626         outcome.setResult(OperationResult.SUCCESS);
627         assertFalse(mgr.isAbort(outcome));
628     }
629
630     @Test
631     public void testAddToHistory() throws ControlLoopException {
632         mgr.start();
633
634         // add a "start" outcome
635         OperationOutcome outcome = makeOutcome();
636         mgr.addToHistory(outcome);
637
638         assertThat(mgr.getPartialHistory()).hasSize(1);
639         assertThat(mgr.getFullHistory()).hasSize(1);
640
641         // add a "completion" outcome - should replace the start
642         outcome = makeCompletedOutcome();
643         mgr.addToHistory(outcome);
644
645         assertThat(mgr.getPartialHistory()).hasSize(1);
646         assertThat(mgr.getFullHistory()).hasSize(1);
647         assertSame(outcome, mgr.getPartialHistory().peek().getOutcome());
648         assertSame(outcome, mgr.getFullHistory().peek().getOutcome());
649
650         // add another start
651         outcome = makeOutcome();
652         mgr.addToHistory(outcome);
653
654         assertThat(mgr.getPartialHistory()).hasSize(2);
655         assertThat(mgr.getFullHistory()).hasSize(2);
656         assertSame(outcome, mgr.getPartialHistory().peekLast().getOutcome());
657         assertSame(outcome, mgr.getFullHistory().peekLast().getOutcome());
658
659         // remove the last item from the full history and then add a "completion"
660         mgr.getFullHistory().removeLast();
661         outcome = makeCompletedOutcome();
662         mgr.addToHistory(outcome);
663         assertThat(mgr.getPartialHistory()).hasSize(2);
664         assertThat(mgr.getFullHistory()).hasSize(2);
665
666         // add another "start"
667         outcome = makeOutcome();
668         mgr.addToHistory(outcome);
669         assertThat(mgr.getPartialHistory()).hasSize(3);
670         assertThat(mgr.getFullHistory()).hasSize(3);
671
672         // add a "completion" for a different actor - should NOT replace the start
673         outcome = makeCompletedOutcome();
674         outcome.setActor("different-actor");
675         mgr.addToHistory(outcome);
676         assertThat(mgr.getPartialHistory()).hasSize(4);
677         assertThat(mgr.getFullHistory()).hasSize(4);
678         assertSame(outcome, mgr.getPartialHistory().peekLast().getOutcome());
679         assertSame(outcome, mgr.getFullHistory().peekLast().getOutcome());
680     }
681
682     @Test
683     public void testMakeNotification() throws Exception {
684         loadPolicy(EVENT_MGR_MULTI_YAML);
685         mgr = new MyManager(params, event, workMem);
686
687         // before started
688         assertNotNull(mgr.makeNotification());
689
690         mgr.start();
691
692         mgr.addToHistory(makeCompletedOutcome());
693         mgr.addToHistory(makeCompletedOutcome());
694         mgr.addToHistory(makeCompletedOutcome());
695
696         // check notification while running
697         VirtualControlLoopNotification notif = mgr.makeNotification();
698         assertThat(notif.getMessage()).contains(SIMPLE_ACTOR);
699         assertThat(notif.getHistory()).hasSize(3);
700
701         // indicate success and load the next policy - should clear the partial history
702         mgr.loadNextPolicy(OperationResult.SUCCESS);
703
704         // check notification
705         notif = mgr.makeNotification();
706         assertNull(notif.getMessage());
707         assertThat(notif.getHistory()).isEmpty();
708
709         // add outcomes and check again
710         mgr.addToHistory(makeCompletedOutcome());
711         mgr.addToHistory(makeCompletedOutcome());
712
713         notif = mgr.makeNotification();
714         assertNotNull(notif.getMessage());
715
716         // should only have history for last two outcomes
717         assertThat(notif.getHistory()).hasSize(2);
718
719         // indicate failure - should go to final state
720         mgr.loadNextPolicy(OperationResult.FAILURE);
721
722         // check notification
723         notif = mgr.makeNotification();
724         assertNull(notif.getMessage());
725
726         // should be no history
727         assertThat(notif.getHistory()).isEmpty();
728
729         // null case
730         assertThatThrownBy(() -> mgr.loadNextPolicy(null)).isInstanceOf(NullPointerException.class)
731                         .hasMessageContaining("lastResult");
732     }
733
734     @Test
735     public void testDeliver() {
736         mgr.deliver(MY_SINK, null, "null notification", "null rule");
737         verify(engineMgr, never()).deliver(any(), any());
738
739         mgr.deliver(MY_SINK, "publishA", "A notification", "A rule");
740         verify(engineMgr).deliver(MY_SINK, "publishA");
741
742         // cause deliver() to throw an exception
743         when(engineMgr.deliver(any(), any())).thenThrow(new IllegalStateException("expected exception"));
744         assertThatCode(() -> mgr.deliver(MY_SINK, "publishB", "B notification", "B rule")).doesNotThrowAnyException();
745     }
746
747     @Test
748     public void testGetOperationMessage() throws ControlLoopException {
749         // no history yet
750         assertNull(mgr.getOperationMessage());
751
752         // add an outcome
753         mgr.start();
754         OperationOutcome outcome = makeOutcome();
755         mgr.addToHistory(outcome);
756
757         assertThat(mgr.getOperationMessage()).contains("actor=" + SIMPLE_ACTOR)
758                         .contains("operation=" + SIMPLE_OPERATION);
759     }
760
761     @Test
762     public void testStoreInDataBase() throws ControlLoopException {
763         mgr.start();
764         OperationOutcome outcome = makeOutcome();
765         mgr.addToHistory(outcome);
766
767         mgr.storeInDataBase(mgr.getPartialHistory().peekLast());
768
769         verify(dataMgr).store(REQ_ID.toString(), event, null, mgr.getPartialHistory().peekLast().getClOperation());
770     }
771
772     @Test
773     public void testMakeControlLoopResponse() {
774         final OperationOutcome outcome = new OperationOutcome();
775
776         // no message - should return null
777         checkResp(outcome, null);
778
779         // not a PciMessage - should return null
780         outcome.setResponse("not-a-pci-message");
781         checkResp(outcome, null);
782
783         /*
784          * now work with a PciMessage
785          */
786         PciMessage msg = new PciMessage();
787         outcome.setResponse(msg);
788
789         PciBody body = new PciBody();
790         msg.setBody(body);
791
792         PciResponse output = new PciResponse();
793         body.setOutput(output);
794
795         output.setPayload("my-payload");
796
797         // should generate a response, with a payload
798         checkResp(outcome, "my-payload");
799
800         /*
801          * these should generate a response, with null payload
802          */
803         output.setPayload(null);
804         checkResp(outcome, null);
805
806         body.setOutput(null);
807         checkResp(outcome, null);
808
809         msg.setBody(null);
810         checkResp(outcome, null);
811
812         outcome.setResponse(null);
813         checkResp(outcome, null);
814     }
815
816     @Test
817     public void testOnNewEvent() {
818         VirtualControlLoopEvent event2 = new VirtualControlLoopEvent(event);
819         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
820
821         event2.setPayload("other payload");
822         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
823         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
824         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event));
825
826         event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
827         assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
828
829         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
830         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
831
832         event2.setClosedLoopEventStatus(null);
833         assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
834     }
835
836     @Test
837     public void testCheckEventSyntax() {
838         // initially, it's valid
839         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
840
841         event.setTarget("unknown-target");
842         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
843                         .hasMessage("target field invalid");
844
845         event.setTarget(null);
846         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
847                         .hasMessage("No target field");
848
849         // abated supersedes previous errors - so it shouldn't throw an exception
850         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
851         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
852
853         event.setRequestId(null);
854         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
855                         .hasMessage("No request ID");
856
857         event.setClosedLoopControlName(null);
858         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
859                         .hasMessage("No control loop name");
860     }
861
862     @Test
863     public void testValidateStatus() {
864         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
865         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
866
867         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
868         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
869
870         event.setClosedLoopEventStatus(null);
871         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
872                         .hasMessage("Invalid value in closedLoopEventStatus");
873     }
874
875     @Test
876     public void testValidateAaiData() {
877         event.setTargetType("unknown-target-type");
878         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
879                         .hasMessage("The target type is not supported");
880
881         event.setTargetType(null);
882         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
883                         .hasMessage("The Target type is null");
884
885         event.setAai(null);
886         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
887                         .hasMessage("AAI is null");
888
889         // VM case
890         event.setTargetType(ControlLoopTargetType.VM);
891         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
892         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
893
894         event.setAai(Map.of());
895         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
896
897         // VNF case
898         event.setTargetType(ControlLoopTargetType.VNF);
899         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
900         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
901
902         event.setAai(Map.of());
903         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
904
905         // PNF case
906         event.setTargetType(ControlLoopTargetType.PNF);
907         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
908         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
909
910         event.setAai(Map.of());
911         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
912     }
913
914     @Test
915     public void testValidateAaiVmVnfData() {
916         event.setTargetType(ControlLoopTargetType.VM);
917         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
918         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
919
920         event.setAai(Map.of(ControlLoopEventManager2.VSERVER_VSERVER_NAME, MY_TARGET));
921         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
922
923         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_NAME, MY_TARGET));
924         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
925
926         event.setAai(Map.of());
927         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class).hasMessage(
928                         "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
929     }
930
931     @Test
932     public void testValidateAaiPnfData() {
933         event.setTargetType(ControlLoopTargetType.PNF);
934         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
935         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
936
937         event.setAai(Map.of());
938         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
939                         .hasMessage("AAI PNF object key pnf-name is missing");
940     }
941
942     @Test
943     public void testIsClosedLoopDisabled() {
944         Map<String, String> orig = event.getAai();
945
946         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
947         assertThatThrownBy(() -> new ControlLoopEventManager2Drools(params, event, workMem))
948                         .isInstanceOf(IllegalStateException.class);
949
950         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "true"));
951         assertThatThrownBy(() -> new ControlLoopEventManager2Drools(params, event, workMem))
952                         .isInstanceOf(IllegalStateException.class);
953
954         event.setAai(addAai(orig, ControlLoopEventManager2.PNF_IS_IN_MAINT, "true"));
955         assertThatThrownBy(() -> new ControlLoopEventManager2Drools(params, event, workMem))
956                         .isInstanceOf(IllegalStateException.class);
957     }
958
959     @Test
960     public void testIsProvStatusInactive() {
961         Map<String, String> orig = event.getAai();
962
963         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "ACTIVE"));
964         assertThatCode(() -> new ControlLoopEventManager2Drools(params, event, workMem)).doesNotThrowAnyException();
965
966         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "inactive"));
967         assertThatThrownBy(() -> new ControlLoopEventManager2Drools(params, event, workMem))
968                         .isInstanceOf(IllegalStateException.class);
969
970         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "ACTIVE"));
971         assertThatCode(() -> new ControlLoopEventManager2Drools(params, event, workMem)).doesNotThrowAnyException();
972
973         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "inactive"));
974         assertThatThrownBy(() -> new ControlLoopEventManager2Drools(params, event, workMem))
975                         .isInstanceOf(IllegalStateException.class);
976     }
977
978     @Test
979     public void testIsAaiTrue() {
980         Map<String, String> orig = event.getAai();
981
982         for (String value : Arrays.asList("yes", "y", "true", "t", "yEs", "trUe")) {
983             event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, value));
984             assertThatThrownBy(() -> new ControlLoopEventManager2Drools(params, event, workMem))
985                             .isInstanceOf(IllegalStateException.class);
986         }
987
988         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "false"));
989         assertThatCode(() -> new ControlLoopEventManager2Drools(params, event, workMem)).doesNotThrowAnyException();
990
991         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "no"));
992         assertThatCode(() -> new ControlLoopEventManager2Drools(params, event, workMem)).doesNotThrowAnyException();
993     }
994
995
996     private Map<String, String> addAai(Map<String, String> original, String key, String value) {
997         Map<String, String> map = new TreeMap<>(original);
998         map.put(key, value);
999         return map;
1000     }
1001
1002     private void loadPolicy(String fileName) throws CoderException {
1003         ToscaServiceTemplate template =
1004                         yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
1005         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
1006
1007         params.setToscaPolicy(tosca);
1008     }
1009
1010     private void loadStepsWithProperties(String... properties) {
1011         stepa = new Step2(mgr, ControlLoopOperationParams.builder().build(), event) {
1012
1013             @Override
1014             public boolean isPolicyStep() {
1015                 return false;
1016             }
1017
1018             @Override
1019             public List<String> getPropertyNames() {
1020                 return List.of(properties);
1021             }
1022
1023             @Override
1024             protected Operation buildOperation() {
1025                 return policyOperation;
1026             }
1027         };
1028
1029         mgr.getSteps().add(stepa);
1030         mgr.getSteps().add(stepb);
1031     }
1032
1033     private OperationOutcome makeCompletedOutcome() {
1034         OperationOutcome outcome = makeOutcome();
1035         outcome.setEnd(outcome.getStart());
1036
1037         return outcome;
1038     }
1039
1040     private OperationOutcome makeOutcome() {
1041         OperationOutcome outcome = new OperationOutcome();
1042         outcome.setActor(SIMPLE_ACTOR);
1043         outcome.setOperation(SIMPLE_OPERATION);
1044         outcome.setMessage(OUTCOME_MSG);
1045         outcome.setResult(OperationResult.SUCCESS);
1046         outcome.setStart(Instant.now());
1047         outcome.setTarget(MY_TARGET);
1048
1049         return outcome;
1050     }
1051
1052     private void checkResp(OperationOutcome outcome, String expectedPayload) {
1053         ControlLoopResponse resp = mgr.makeControlLoopResponse(outcome);
1054         assertNotNull(resp);
1055         assertEquals(REQ_ID, resp.getRequestId());
1056         assertEquals(expectedPayload, resp.getPayload());
1057     }
1058
1059     /**
1060      * Sets the target entity so a step doesn't have to be added to set it.
1061      */
1062     private void setTargetEntity() {
1063         mgr.setProperty(OperationProperties.AAI_TARGET_ENTITY, MY_TARGET);
1064     }
1065
1066
1067     private class MyManager extends UsecasesEventManager {
1068         private static final long serialVersionUID = 1L;
1069
1070         public MyManager(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
1071                         throws ControlLoopException {
1072
1073             super(params, event, workMem);
1074         }
1075
1076         @Override
1077         protected ExecutorService getBlockingExecutor() {
1078             return executor;
1079         }
1080
1081         @Override
1082         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
1083             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
1084             locks.add(lock);
1085             callback.lockAvailable(lock);
1086         }
1087
1088         @Override
1089         public ActorService getActorService() {
1090             return actors;
1091         }
1092
1093         @Override
1094         public OperationHistoryDataManager getDataManager() {
1095             return dataMgr;
1096         }
1097
1098         @Override
1099         protected PolicyEngine getPolicyEngineManager() {
1100             return engineMgr;
1101         }
1102     }
1103 }