0649f18823b0f1f2aa2f488b6456363f70aa8e76
[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.onap.policy.controlloop.ControlLoopTargetType.PNF;
24 import static org.onap.policy.controlloop.ControlLoopTargetType.VM;
25 import static org.onap.policy.controlloop.ControlLoopTargetType.VNF;
26 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED;
27 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.GENERIC_VNF_PROV_STATUS;
28 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.GENERIC_VNF_VNF_ID;
29 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.GENERIC_VNF_VNF_NAME;
30 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.PNF_IS_IN_MAINT;
31 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.PNF_NAME;
32 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.PROV_STATUS_ACTIVE;
33 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.VM_NAME;
34 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.VNF_NAME;
35 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED;
36 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.VSERVER_PROV_STATUS;
37 import static org.onap.policy.drools.apps.controller.usecases.UsecasesConstants.VSERVER_VSERVER_NAME;
38
39 import java.util.ArrayDeque;
40 import java.util.Deque;
41 import java.util.LinkedHashMap;
42 import java.util.LinkedList;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.stream.Collectors;
46 import java.util.stream.Stream;
47 import lombok.Getter;
48 import lombok.NonNull;
49 import lombok.Setter;
50 import lombok.ToString;
51 import org.apache.commons.lang3.StringUtils;
52 import org.drools.core.WorkingMemory;
53 import org.kie.api.runtime.rule.FactHandle;
54 import org.onap.policy.controlloop.ControlLoopEventStatus;
55 import org.onap.policy.controlloop.ControlLoopException;
56 import org.onap.policy.controlloop.ControlLoopNotificationType;
57 import org.onap.policy.controlloop.ControlLoopOperation;
58 import org.onap.policy.controlloop.ControlLoopResponse;
59 import org.onap.policy.controlloop.VirtualControlLoopEvent;
60 import org.onap.policy.controlloop.VirtualControlLoopNotification;
61 import org.onap.policy.controlloop.actorserviceprovider.OperationFinalResult;
62 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
63 import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
64 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
65 import org.onap.policy.controlloop.actorserviceprovider.TargetType;
66 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
67 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
68 import org.onap.policy.controlloop.eventmanager.ActorConstants;
69 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
70 import org.onap.policy.controlloop.eventmanager.StepContext;
71 import org.onap.policy.drools.apps.controller.usecases.step.AaiCqStep2;
72 import org.onap.policy.drools.apps.controller.usecases.step.AaiGetPnfStep2;
73 import org.onap.policy.drools.apps.controller.usecases.step.AaiGetTenantStep2;
74 import org.onap.policy.drools.apps.controller.usecases.step.GetTargetEntityStep2;
75 import org.onap.policy.drools.apps.controller.usecases.step.GuardStep2;
76 import org.onap.policy.drools.apps.controller.usecases.step.LockStep2;
77 import org.onap.policy.drools.apps.controller.usecases.step.Step2;
78 import org.onap.policy.drools.domain.models.operational.ActorOperation;
79 import org.onap.policy.drools.domain.models.operational.Operation;
80 import org.onap.policy.drools.domain.models.operational.OperationalTarget;
81 import org.onap.policy.drools.system.PolicyEngine;
82 import org.onap.policy.drools.system.PolicyEngineConstants;
83 import org.onap.policy.sdnr.PciMessage;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
86
87 /**
88  * Manager for a single control loop event. Once this has been created, the event can be
89  * retracted from working memory. Processing progresses through each policy, which
90  * involves at least one step. As a step is processed, additional preprocessor steps may
91  * be pushed onto the queue (e.g., locks, A&AI queries, guards).
92  */
93 @ToString(onlyExplicitlyIncluded = true)
94 public class UsecasesEventManager extends ControlLoopEventManager implements StepContext {
95
96     private static final Logger logger = LoggerFactory.getLogger(UsecasesEventManager.class);
97     private static final long serialVersionUID = -1216568161322872641L;
98
99     /**
100      * Maximum number of steps, for a single policy, allowed in the queue at a time. This
101      * prevents an infinite loop occurring with calls to {@link #loadPreprocessorSteps()}.
102      */
103     public static final int MAX_STEPS = 30;
104
105     /**
106      * If there's a failure from one of these actors, then the TOSCA processing should be
107      * aborted.
108      */
109     private static final Set<String> ABORT_ACTORS = Set.of(ActorConstants.CL_TIMEOUT_ACTOR, ActorConstants.LOCK_ACTOR);
110
111     private static final Set<String> VALID_TARGETS = Stream
112                     .of(VM_NAME, VNF_NAME, VSERVER_VSERVER_NAME, GENERIC_VNF_VNF_ID, GENERIC_VNF_VNF_NAME, PNF_NAME)
113                     .map(String::toLowerCase).collect(Collectors.toSet());
114
115     private static final Set<String> TRUE_VALUES = Set.of("true", "t", "yes", "y");
116
117     /**
118      * Names of Operation properties for which A&AI PNF query is needed.
119      */
120     private static final Set<String> PNF_PROPERTIES = Set.of(OperationProperties.AAI_PNF);
121
122     /**
123      * Names of Operation properties for which A&AI Tenant query is needed.
124      */
125     private static final Set<String> TENANT_PROPERTIES = Set.of(OperationProperties.AAI_VSERVER_LINK);
126
127     /**
128      * Names of Operation properties for which A&AI custom query is needed.
129      */
130     private static final Set<String> CQ_PROPERTIES = Set.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION,
131                     OperationProperties.AAI_VNF, OperationProperties.AAI_SERVICE_MODEL,
132                     OperationProperties.AAI_VNF_MODEL, OperationProperties.AAI_SERVICE,
133                     OperationProperties.AAI_RESOURCE_VNF, UsecasesConstants.AAI_DEFAULT_GENERIC_VNF);
134
135     public enum State {
136         LOAD_POLICY, POLICY_LOADED, AWAITING_OUTCOME, DONE
137     }
138
139     public enum NewEventStatus {
140         FIRST_ONSET, SUBSEQUENT_ONSET, FIRST_ABATEMENT, SUBSEQUENT_ABATEMENT, SYNTAX_ERROR
141     }
142
143     @Getter
144     private final VirtualControlLoopEvent event;
145
146     /**
147      * Request ID, as a String.
148      */
149     private final String requestIdStr;
150
151     @Getter
152     @Setter
153     private State state;
154
155     /**
156      * {@code True} if the event has been accepted (i.e., an "ACTIVE" notification has
157      * been delivered), {@code false} otherwise.
158      */
159     @Getter
160     @Setter
161     private boolean accepted;
162
163     /**
164      * Queue of steps waiting to be performed.
165      */
166     @Getter
167     private final transient Deque<Step2> steps = new ArrayDeque<>(6);
168
169     /**
170      * Number of attempts, so far, for the current step.
171      */
172     @Getter
173     private int attempts;
174
175     /**
176      * Policy currently being processed.
177      */
178     private Operation policy;
179
180     /**
181      * Result of the last policy operation. This is just a place where the rules can store
182      * the value for passing to {@link #loadNextPolicy()}.
183      */
184     @Getter
185     @Setter
186     private OperationResult result = OperationResult.SUCCESS;
187
188     @ToString.Include
189     private int numOnsets = 1;
190     @ToString.Include
191     private int numAbatements = 0;
192
193     private VirtualControlLoopEvent abatement = null;
194
195     /**
196      * Full history of operations that have been processed by the rules. This includes the
197      * items in {@link #partialHistory}.
198      */
199     @Getter
200     private final transient Deque<OperationOutcome2> fullHistory = new LinkedList<>();
201
202     /**
203      * History of operations that have been processed by the rules for the current policy.
204      * When a step is started, its "start" outcome is added. However, once it completes,
205      * its "start" outcome is removed and the "completed" outcome is added.
206      */
207     @Getter
208     private final transient Deque<OperationOutcome2> partialHistory = new LinkedList<>();
209
210     @Getter
211     private OperationFinalResult finalResult = null;
212
213     /**
214      * Message to be placed into the final notification. Typically used when something
215      * causes processing to abort.
216      */
217     @Getter
218     private String finalMessage = null;
219
220     private final transient WorkingMemory workMem;
221     private transient FactHandle factHandle;
222
223
224     /**
225      * Constructs the object.
226      *
227      * @param params control loop parameters
228      * @param event event to be managed by this object
229      * @param workMem working memory to update if this changes
230      * @throws ControlLoopException if the event is invalid or if a YAML processor cannot
231      *         be created
232      */
233     public UsecasesEventManager(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
234                     throws ControlLoopException {
235
236         super(params, event.getRequestId());
237
238         checkEventSyntax(event);
239
240         if (isClosedLoopDisabled(event)) {
241             throw new IllegalStateException("is-closed-loop-disabled is set to true on VServer or VNF");
242         }
243
244         if (isProvStatusInactive(event)) {
245             throw new IllegalStateException("prov-status is not ACTIVE on VServer or VNF");
246         }
247
248         this.event = event;
249         this.workMem = workMem;
250         this.requestIdStr = getRequestId().toString();
251     }
252
253     @Override
254     public void destroy() {
255         for (Step2 step : steps) {
256             step.cancel();
257         }
258
259         super.destroy();
260     }
261
262     /**
263      * Starts the manager and loads the first policy.
264      *
265      * @throws ControlLoopException if the processor cannot get a policy
266      */
267     public void start() throws ControlLoopException {
268         if (!isActive()) {
269             throw new IllegalStateException("manager is no longer active");
270         }
271
272         if ((factHandle = workMem.getFactHandle(this)) == null) {
273             throw new IllegalStateException("manager is not in working memory");
274         }
275
276         if (!steps.isEmpty()) {
277             throw new IllegalStateException("manager already started");
278         }
279
280         loadPolicy();
281     }
282
283     /**
284      * Indicates that processing has been aborted.
285      *
286      * @param finalState final state
287      * @param finalResult final result
288      * @param finalMessage final message
289      */
290     public void abort(@NonNull State finalState, OperationFinalResult finalResult, String finalMessage) {
291         this.state = finalState;
292         this.finalResult = finalResult;
293         this.finalMessage = finalMessage;
294     }
295
296     /**
297      * Loads the next policy.
298      *
299      * @param lastResult result from the last policy
300      *
301      * @throws ControlLoopException if the processor cannot get a policy
302      */
303     public void loadNextPolicy(@NonNull OperationResult lastResult) throws ControlLoopException {
304         getProcessor().nextPolicyForResult(lastResult);
305         loadPolicy();
306     }
307
308     /**
309      * Loads the current policy.
310      *
311      * @throws ControlLoopException if the processor cannot get a policy
312      */
313     private void loadPolicy() throws ControlLoopException {
314         partialHistory.clear();
315
316         if ((finalResult = getProcessor().checkIsCurrentPolicyFinal()) != null) {
317             // final policy - nothing more to do
318             return;
319         }
320
321         policy = getProcessor().getCurrentPolicy();
322
323         ActorOperation actor = policy.getActorOperation();
324
325         OperationalTarget target = actor.getTarget();
326         String targetType = (target != null ? target.getTargetType() : null);
327         Map<String, String> entityIds = (target != null ? target.getEntityIds() : null);
328
329         // convert policy payload from Map<String,String> to Map<String,Object>
330         Map<String, Object> payload = new LinkedHashMap<>();
331         if (actor.getPayload() != null) {
332             payload.putAll(actor.getPayload());
333         }
334
335         // @formatter:off
336         ControlLoopOperationParams params = ControlLoopOperationParams.builder()
337                         .actorService(getActorService())
338                         .actor(actor.getActor())
339                         .operation(actor.getOperation())
340                         .requestId(event.getRequestId())
341                         .executor(getExecutor())
342                         .retry(policy.getRetries())
343                         .timeoutSec(policy.getTimeout())
344                         .targetType(TargetType.toTargetType(targetType))
345                         .targetEntityIds(entityIds)
346                         .payload(payload)
347                         .startCallback(this::onStart)
348                         .completeCallback(this::onComplete)
349                         .build();
350         // @formatter:on
351
352         // load the policy's operation
353         steps.add(new Step2(this, params, event));
354     }
355
356     /**
357      * Loads the preprocessor steps needed by the step that's at the front of the queue.
358      */
359     public void loadPreprocessorSteps() {
360         if (steps.size() >= MAX_STEPS) {
361             throw new IllegalStateException("too many steps");
362         }
363
364         final Step2 step = steps.peek();
365
366         // initialize the step so we can query its properties
367         step.init();
368
369         // determine if any A&AI queries are needed
370         boolean needCq = false;
371         boolean needPnf = false;
372         boolean needTenant = false;
373         boolean needTargetEntity = false;
374
375         for (String propName : step.getPropertyNames()) {
376             needCq = needCq || CQ_PROPERTIES.contains(propName);
377             needPnf = needPnf || PNF_PROPERTIES.contains(propName);
378             needTenant = needTenant || TENANT_PROPERTIES.contains(propName);
379             needTargetEntity = needTargetEntity || OperationProperties.AAI_TARGET_ENTITY.equals(propName);
380         }
381
382         /*
383          * The Policy's actual operation requires additional, implicit steps, such as
384          * locking and guards.
385          */
386         final boolean needPolicySteps = step.isPolicyStep();
387
388
389         /*
390          * NOTE: need to push steps onto the queue in the OPPOSITE order in which they
391          * must be performed.
392          */
393
394
395         // GUARD must be pushed first
396         if (needPolicySteps) {
397             steps.push(new GuardStep2(step, getClosedLoopControlName()));
398         }
399
400         // A&AI queries
401         if (needCq) {
402             steps.push(new AaiCqStep2(step));
403         }
404
405         if (needPnf) {
406             steps.push(new AaiGetPnfStep2(step));
407         }
408
409         if (needTenant) {
410             steps.push(new AaiGetTenantStep2(step));
411         }
412
413         // LOCK must be pushed after the queries
414         if (needPolicySteps) {
415             steps.push(new LockStep2(step));
416         }
417
418         // GET-TARGET-ENTITY should be pushed last
419         if (needTargetEntity) {
420             steps.push(new GetTargetEntityStep2(step));
421         }
422     }
423
424     /**
425      * Executes the first step in the queue.
426      *
427      * @return {@code true} if the step was started, {@code false} if it is no longer
428      *         needed (or if the queue is empty)
429      */
430     public boolean executeStep() {
431         attempts = 0;
432
433         Step2 step = steps.peek();
434         if (step == null) {
435             return false;
436         }
437
438         return step.start(getEndTimeMs() - System.currentTimeMillis());
439     }
440
441     /**
442      * Discards the current step, if any.
443      */
444     public void nextStep() {
445         steps.poll();
446     }
447
448     /**
449      * Increments the number of attempts.
450      */
451     public void bumpAttempts() {
452         ++attempts;
453     }
454
455     /**
456      * Determines if the TOSCA should be aborted due to the given outcome.
457      *
458      * @param outcome outcome to examine
459      * @return {@code true} if the TOSCA should be aborted, {@code false} otherwise
460      */
461     public boolean isAbort(OperationOutcome outcome) {
462         return (outcome.getResult() != OperationResult.SUCCESS && ABORT_ACTORS.contains(outcome.getActor()));
463     }
464
465     /**
466      * Adds the outcome to the history.
467      *
468      * @param outcome outcome to add
469      */
470     public void addToHistory(OperationOutcome outcome) {
471         OperationOutcome2 last = partialHistory.peekLast();
472
473         if (last != null && last.getOutcome().getEnd() == null
474                         && last.getOutcome().isFor(outcome.getActor(), outcome.getOperation())) {
475             // last item was a "start" - remove it
476             partialHistory.removeLast();
477
478             if (fullHistory.peekLast() == last) {
479                 fullHistory.removeLast();
480             }
481         }
482
483         OperationOutcome2 outcome2 = new OperationOutcome2(outcome);
484         partialHistory.add(outcome2);
485         fullHistory.add(outcome2);
486     }
487
488     /**
489      * Makes a notification message for the current operation.
490      *
491      * @return a new notification
492      */
493     public VirtualControlLoopNotification makeNotification() {
494         VirtualControlLoopNotification notif = new VirtualControlLoopNotification(event);
495         notif.setNotification(ControlLoopNotificationType.OPERATION);
496         notif.setFrom("policy");
497         notif.setPolicyVersion(getPolicyVersion());
498
499         if (finalResult != null) {
500             return notif;
501         }
502
503         OperationOutcome2 last = partialHistory.peekLast();
504         if (last == null) {
505             return notif;
506         }
507
508         notif.setMessage(last.getClOperation().toHistory());
509         notif.setHistory(partialHistory.stream().map(OperationOutcome2::getClOperation).collect(Collectors.toList()));
510
511         return notif;
512     }
513
514     /**
515      * Delivers a notification to a topic.
516      *
517      * @param sinkName name of the topic sink
518      * @param notification notification to be published, or {@code null} if nothing is to
519      *        be published
520      * @param notificationType type of notification, used when logging error messages
521      * @param ruleName name of the rule doing the publishing
522      */
523     public <T> void deliver(String sinkName, T notification, String notificationType, String ruleName) {
524         try {
525             if (notification != null) {
526                 getPolicyEngineManager().deliver(sinkName, notification);
527             }
528
529         } catch (RuntimeException e) {
530             logger.warn("{}: {}.{}: manager={} exception publishing {}", getClosedLoopControlName(), getPolicyName(),
531                             ruleName, this, notificationType, e);
532         }
533     }
534
535     /**
536      * Get the last operation, as a message.
537      *
538      * @return the last operation, as a message
539      */
540     public String getOperationMessage() {
541         OperationOutcome2 last = fullHistory.peekLast();
542         return (last == null ? null : last.getClOperation().toMessage());
543     }
544
545     /**
546      * Stores an operation outcome in the DB.
547      *
548      * @param outcome operation outcome to store
549      */
550     public void storeInDataBase(OperationOutcome2 outcome) {
551         String targetEntity = getProperty(OperationProperties.AAI_TARGET_ENTITY);
552
553         getDataManager().store(requestIdStr, event, targetEntity, outcome.getClOperation());
554     }
555
556     /**
557      * Makes a control loop response.
558      *
559      * @param outcome operation outcome
560      * @return a new control loop response, or {@code null} if none is required
561      */
562     public ControlLoopResponse makeControlLoopResponse(OperationOutcome outcome) {
563         ControlLoopResponse clRsp = new ControlLoopResponse();
564         clRsp.setFrom(outcome.getActor());
565         clRsp.setTarget("DCAE");
566         clRsp.setClosedLoopControlName(event.getClosedLoopControlName());
567         clRsp.setPolicyName(event.getPolicyName());
568         clRsp.setPolicyVersion(event.getPolicyVersion());
569         clRsp.setRequestId(event.getRequestId());
570         clRsp.setVersion(event.getVersion());
571
572         Object obj = outcome.getResponse();
573         if (!(obj instanceof PciMessage)) {
574             return clRsp;
575         }
576
577         PciMessage msg = (PciMessage) obj;
578         if (msg.getBody() != null && msg.getBody().getOutput() != null) {
579             clRsp.setPayload(msg.getBody().getOutput().getPayload());
580         }
581
582         return clRsp;
583     }
584
585     /**
586      * An event onset/abatement.
587      *
588      * @param newEvent the event
589      * @return the status
590      */
591     public NewEventStatus onNewEvent(VirtualControlLoopEvent newEvent) {
592         try {
593             checkEventSyntax(newEvent);
594
595             if (newEvent.getClosedLoopEventStatus() == ControlLoopEventStatus.ONSET) {
596                 if (newEvent.equals(event)) {
597                     return NewEventStatus.FIRST_ONSET;
598                 }
599
600                 numOnsets++;
601                 return NewEventStatus.SUBSEQUENT_ONSET;
602
603             } else {
604                 if (abatement == null) {
605                     abatement = newEvent;
606                     numAbatements++;
607                     return NewEventStatus.FIRST_ABATEMENT;
608                 } else {
609                     numAbatements++;
610                     return NewEventStatus.SUBSEQUENT_ABATEMENT;
611                 }
612             }
613         } catch (ControlLoopException e) {
614             logger.error("{}: onNewEvent threw an exception", this, e);
615             return NewEventStatus.SYNTAX_ERROR;
616         }
617     }
618
619     /**
620      * Check an event syntax.
621      *
622      * @param event the event syntax
623      * @throws ControlLoopException if an error occurs
624      */
625     protected void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
626         validateStatus(event);
627         if (StringUtils.isBlank(event.getClosedLoopControlName())) {
628             throw new ControlLoopException("No control loop name");
629         }
630         if (event.getRequestId() == null) {
631             throw new ControlLoopException("No request ID");
632         }
633         if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
634             return;
635         }
636         if (StringUtils.isBlank(event.getTarget())) {
637             throw new ControlLoopException("No target field");
638         } else if (!VALID_TARGETS.contains(event.getTarget().toLowerCase())) {
639             throw new ControlLoopException("target field invalid");
640         }
641         validateAaiData(event);
642     }
643
644     private void validateStatus(VirtualControlLoopEvent event) throws ControlLoopException {
645         if (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET
646                         && event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED) {
647             throw new ControlLoopException("Invalid value in closedLoopEventStatus");
648         }
649     }
650
651     private void validateAaiData(VirtualControlLoopEvent event) throws ControlLoopException {
652         Map<String, String> eventAai = event.getAai();
653         if (eventAai == null) {
654             throw new ControlLoopException("AAI is null");
655         }
656         if (event.getTargetType() == null) {
657             throw new ControlLoopException("The Target type is null");
658         }
659         switch (event.getTargetType()) {
660             case VM:
661             case VNF:
662                 validateAaiVmVnfData(eventAai);
663                 return;
664             case PNF:
665                 validateAaiPnfData(eventAai);
666                 return;
667             default:
668                 throw new ControlLoopException("The target type is not supported");
669         }
670     }
671
672     private void validateAaiVmVnfData(Map<String, String> eventAai) throws ControlLoopException {
673         if (eventAai.get(GENERIC_VNF_VNF_ID) == null && eventAai.get(VSERVER_VSERVER_NAME) == null
674                         && eventAai.get(GENERIC_VNF_VNF_NAME) == null) {
675             throw new ControlLoopException(
676                             "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
677         }
678     }
679
680     private void validateAaiPnfData(Map<String, String> eventAai) throws ControlLoopException {
681         if (eventAai.get(PNF_NAME) == null) {
682             throw new ControlLoopException("AAI PNF object key pnf-name is missing");
683         }
684     }
685
686     /**
687      * Is closed loop disabled for an event.
688      *
689      * @param event the event
690      * @return <code>true</code> if the control loop is disabled, <code>false</code>
691      *         otherwise
692      */
693     private static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
694         Map<String, String> aai = event.getAai();
695         return (isAaiTrue(aai.get(VSERVER_IS_CLOSED_LOOP_DISABLED))
696                         || isAaiTrue(aai.get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED))
697                         || isAaiTrue(aai.get(PNF_IS_IN_MAINT)));
698     }
699
700     /**
701      * Does provisioning status, for an event, have a value other than ACTIVE.
702      *
703      * @param event the event
704      * @return {@code true} if the provisioning status is neither ACTIVE nor {@code null},
705      *         {@code false} otherwise
706      */
707     private static boolean isProvStatusInactive(VirtualControlLoopEvent event) {
708         Map<String, String> aai = event.getAai();
709         return !(PROV_STATUS_ACTIVE.equalsIgnoreCase(aai.getOrDefault(VSERVER_PROV_STATUS, PROV_STATUS_ACTIVE))
710                         && PROV_STATUS_ACTIVE.equalsIgnoreCase(
711                                         aai.getOrDefault(GENERIC_VNF_PROV_STATUS, PROV_STATUS_ACTIVE)));
712     }
713
714     /**
715      * Determines the boolean value represented by the given AAI field value.
716      *
717      * @param aaiValue value to be examined
718      * @return the boolean value represented by the field value, or {@code false} if the
719      *         value is {@code null}
720      */
721     private static boolean isAaiTrue(String aaiValue) {
722         return (aaiValue != null && TRUE_VALUES.contains(aaiValue.toLowerCase()));
723     }
724
725     @Override
726     public void onStart(OperationOutcome outcome) {
727         super.onStart(outcome);
728         workMem.update(factHandle, this);
729     }
730
731     @Override
732     public void onComplete(OperationOutcome outcome) {
733         super.onComplete(outcome);
734         workMem.update(factHandle, this);
735     }
736
737     @Getter
738     @ToString
739     public class OperationOutcome2 {
740         private final int attempt;
741         private final OperationOutcome outcome;
742         private final ControlLoopOperation clOperation;
743
744         /**
745          * Constructs the object.
746          *
747          * @param outcome outcome of the operation
748          */
749         public OperationOutcome2(OperationOutcome outcome) {
750             this.outcome = outcome;
751             this.attempt = attempts;
752
753             clOperation = outcome.toControlLoopOperation();
754
755             // TODO encode()?
756             OperationalTarget target = policy.getActorOperation().getTarget();
757             String targetStr = (target != null ? target.toString() : null);
758             clOperation.setTarget(targetStr);
759
760             if (outcome.getEnd() == null) {
761                 clOperation.setOutcome("Started");
762             } else if (clOperation.getOutcome() == null) {
763                 clOperation.setOutcome("");
764             }
765         }
766     }
767
768     // these following methods may be overridden by junit tests
769
770     protected PolicyEngine getPolicyEngineManager() {
771         return PolicyEngineConstants.getManager();
772     }
773 }