2a5e64d97262297ddef81025c376b48e58982ca3
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * controlloop operation manager
4  * ================================================================================
5  * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved.
7  * Modifications Copyright (C) 2019 Tech Mahindra
8  * Modifications Copyright (C) 2019 Bell Canada.
9  * ================================================================================
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.policy.controlloop.eventmanager;
25
26 import java.io.Serializable;
27 import java.sql.Timestamp;
28 import java.time.Instant;
29 import java.util.AbstractMap;
30 import java.util.HashMap;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.NoSuchElementException;
35 import java.util.Optional;
36 import java.util.Properties;
37 import javax.persistence.EntityManager;
38 import javax.persistence.Persistence;
39
40 import org.apache.commons.lang3.tuple.Pair;
41 import org.eclipse.persistence.config.PersistenceUnitProperties;
42 import org.onap.aai.domain.yang.GenericVnf;
43 import org.onap.aai.domain.yang.ServiceInstance;
44 import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
45 import org.onap.policy.aai.AaiCqResponse;
46 import org.onap.policy.aai.util.AaiException;
47 import org.onap.policy.appc.Response;
48 import org.onap.policy.appc.ResponseCode;
49 import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
50 import org.onap.policy.cds.CdsResponse;
51 import org.onap.policy.controlloop.ControlLoopEvent;
52 import org.onap.policy.controlloop.ControlLoopException;
53 import org.onap.policy.controlloop.ControlLoopOperation;
54 import org.onap.policy.controlloop.ControlLoopResponse;
55 import org.onap.policy.controlloop.VirtualControlLoopEvent;
56 import org.onap.policy.controlloop.actor.appc.AppcActorServiceProvider;
57 import org.onap.policy.controlloop.actor.appclcm.AppcLcmActorServiceProvider;
58 import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider;
59 import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants;
60 import org.onap.policy.controlloop.actor.sdnc.SdncActorServiceProvider;
61 import org.onap.policy.controlloop.actor.sdnr.SdnrActorServiceProvider;
62 import org.onap.policy.controlloop.actor.so.SoActorServiceProvider;
63 import org.onap.policy.controlloop.actor.vfc.VfcActorServiceProvider;
64 import org.onap.policy.controlloop.policy.Policy;
65 import org.onap.policy.controlloop.policy.PolicyResult;
66 import org.onap.policy.controlloop.policy.TargetType;
67 import org.onap.policy.database.operationshistory.Dbao;
68 import org.onap.policy.drools.system.PolicyEngineConstants;
69 import org.onap.policy.guard.Util;
70 import org.onap.policy.sdnc.SdncResponse;
71 import org.onap.policy.sdnr.PciResponseWrapper;
72 import org.onap.policy.so.SoResponseWrapper;
73 import org.onap.policy.vfc.VfcResponse;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 public class ControlLoopOperationManager implements Serializable {
78     private static final String SUCCESS_MSG = " Success";
79     private static final String FAILED_MSG = " Failed";
80     private static final long serialVersionUID = -3773199283624595410L;
81     private static final Logger logger = LoggerFactory.getLogger(ControlLoopOperationManager.class);
82
83     private static final String VSERVER_VSERVER_NAME = "vserver.vserver-name";
84     private static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name";
85     private static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id";
86
87     private static final String AAI_SERVICE_INSTANCE_ID_KEY = "service-instance.service-instance-id";
88     private static final String PNF_NAME = "pnf.pnf-name";
89
90     //
91     // These properties are not changeable, but accessible
92     // for Drools Rule statements.
93     //
94     public final ControlLoopEvent onset;
95     public final Policy policy;
96
97     //
98     // Properties used to track the Operation
99     //
100     private int attempts = 0;
101     private Operation currentOperation = null;
102     private LinkedList<Operation> operationHistory = new LinkedList<>();
103     private PolicyResult policyResult = null;
104     private ControlLoopEventManager eventManager = null;
105     private String targetEntity;
106     private String guardApprovalStatus = "NONE";// "NONE", "PERMIT", "DENY"
107     private AaiCqResponse aaiCqResponse;
108     private transient Object operationRequest;
109
110     /**
111      * Construct an instance.
112      *
113      * @param onset the onset event
114      * @param policy the policy
115      * @param em the event manager
116      * @throws ControlLoopException if an error occurs
117      */
118     public ControlLoopOperationManager(ControlLoopEvent onset, Policy policy,
119         ControlLoopEventManager em) throws ControlLoopException {
120
121         this.onset = onset;
122         this.policy = policy;
123         this.guardApprovalStatus = "NONE";
124         this.eventManager = em;
125
126         try {
127             if (TargetType.VNF.equals(policy.getTarget().getType())
128                 || TargetType.VFMODULE.equals(policy.getTarget().getType())) {
129                 this.aaiCqResponse =
130                     this.eventManager.getCqResponse((VirtualControlLoopEvent) onset);
131             }
132
133             this.targetEntity = getTarget(policy);
134
135             initActor(policy);
136
137         } catch (AaiException e) {
138             throw new ControlLoopException(e.getMessage(), e);
139         }
140     }
141
142     private void initActor(Policy policy) throws AaiException, ControlLoopException {
143         //
144         // Let's make a sanity check
145         //
146         switch (policy.getActor()) {
147             case "APPC":
148                 initAppc(policy);
149                 break;
150             case "SO":
151                 break;
152             case "SDNR":
153                 break;
154             case "VFC":
155                 break;
156             case "SDNC":
157                 break;
158             case "CDS":
159                 break;
160             default:
161                 throw new ControlLoopException(
162                     "ControlLoopEventManager: policy has an unknown actor.");
163         }
164     }
165
166     private void initAppc(Policy policy) throws AaiException {
167         if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) {
168             /*
169              * The target vnf-id may not be the same as the source vnf-id specified in the yaml, the
170              * target
171              * vnf-id is retrieved by a named query to A&AI.
172              */
173             GenericVnf genvnf = this.aaiCqResponse
174                 .getGenericVnfByModelInvariantId(policy.getTarget().getResourceID());
175             if (genvnf == null) {
176                 logger.info("Target entity could not be found");
177                 throw new AaiException("Target vnf-id could not be found");
178             }
179             this.targetEntity = genvnf.getVnfId();
180
181         }
182     }
183
184     public ControlLoopEventManager getEventManager() {
185         return eventManager;
186     }
187
188     public void setEventManager(ControlLoopEventManager eventManager) {
189         this.eventManager = eventManager;
190     }
191
192     public String getTargetEntity() {
193         return this.targetEntity;
194     }
195
196     @Override
197     public String toString() {
198         return "ControlLoopOperationManager [onset="
199             + (onset != null ? onset.getRequestId() : "null") + ", policy="
200             + (policy != null ? policy.getId() : "null") + ", attempts=" + attempts
201             + ", policyResult=" + policyResult + ", currentOperation=" + currentOperation
202             + ", operationHistory=" + operationHistory + "]";
203     }
204
205     //
206     // Internal class used for tracking
207     //
208     private class Operation implements Serializable {
209         private static final long serialVersionUID = 1L;
210
211         private ControlLoopOperation clOperation = new ControlLoopOperation();
212         private PolicyResult policyResult = null;
213         private int attempt = 0;
214
215         @Override
216         public String toString() {
217             return "Operation [attempt=" + attempt + ", policyResult=" + policyResult
218                 + ", operation=" + clOperation + "]";
219         }
220     }
221
222     public Object getOperationRequest() {
223         return operationRequest;
224     }
225
226     public String getGuardApprovalStatus() {
227         return guardApprovalStatus;
228     }
229
230     public void setGuardApprovalStatus(String guardApprovalStatus) {
231         this.guardApprovalStatus = guardApprovalStatus;
232     }
233
234     /**
235      * Get the target for a policy.
236      *
237      * @param policy the policy
238      * @return the target
239      * @throws ControlLoopException if an error occurs
240      */
241     public String getTarget(Policy policy) throws ControlLoopException {
242         if (policy.getTarget() == null) {
243             throw new ControlLoopException("The target is null");
244         }
245
246         if (policy.getTarget().getType() == null) {
247             throw new ControlLoopException("The target type is null");
248         }
249
250         switch (policy.getTarget().getType()) {
251             case PNF:
252                 return getPnfTarget();
253             case VM:
254             case VNF:
255                 return getVfModuleTarget();
256             case VFMODULE:
257                 return getVfModuleTarget();
258             default:
259                 throw new ControlLoopException("The target type is not supported");
260         }
261     }
262
263     private String getVfModuleTarget() throws ControlLoopException {
264         VirtualControlLoopEvent virtualOnsetEvent = (VirtualControlLoopEvent) this.onset;
265         if (this.onset.getTarget().equalsIgnoreCase(VSERVER_VSERVER_NAME)) {
266             return virtualOnsetEvent.getAai().get(VSERVER_VSERVER_NAME);
267         } else if (this.onset.getTarget().equalsIgnoreCase(GENERIC_VNF_VNF_ID)) {
268             return virtualOnsetEvent.getAai().get(GENERIC_VNF_VNF_ID);
269         } else if (this.onset.getTarget().equalsIgnoreCase(GENERIC_VNF_VNF_NAME)) {
270             /*
271              * If the onset is enriched with the vnf-id, we don't need an A&AI response
272              */
273             if (virtualOnsetEvent.getAai().containsKey(GENERIC_VNF_VNF_ID)) {
274                 return virtualOnsetEvent.getAai().get(GENERIC_VNF_VNF_ID);
275             }
276
277             /*
278              * If the vnf-name was retrieved from the onset then the vnf-id must be obtained from
279              * the event
280              * manager's A&AI GET query
281              */
282             try {
283                 String vnfId = this.aaiCqResponse.getDefaultGenericVnf().getVnfId();
284                 if (vnfId == null) {
285                     throw new AaiException("No vnf-id found");
286                 }
287                 return vnfId;
288
289             } catch (AaiException e) {
290                 throw new ControlLoopException(e.getMessage(), e);
291             }
292         }
293         throw new ControlLoopException("Target does not match target type");
294     }
295
296     private String getPnfTarget() throws ControlLoopException {
297         VirtualControlLoopEvent virtualOnsetEvent = (VirtualControlLoopEvent) this.onset;
298         if (!PNF_NAME.equalsIgnoreCase(onset.getTarget())) {
299             throw new ControlLoopException(
300                 "Target in the onset event is either null or does not match target key expected in AAI section.");
301         }
302         return virtualOnsetEvent.getAai().get(PNF_NAME);
303     }
304
305     /**
306      * Start an operation.
307      *
308      * @param onset the onset event
309      * @return the operation request
310      * @throws ControlLoopException if an error occurs
311      */
312     public Object startOperation(/* VirtualControlLoopEvent */ControlLoopEvent onset)
313         throws ControlLoopException {
314         verifyOperatonCanRun();
315
316         //
317         // Setup
318         //
319         this.policyResult = null;
320         Operation operation = new Operation();
321         operation.attempt = ++this.attempts;
322         operation.clOperation.setActor(this.policy.getActor());
323         operation.clOperation.setOperation(this.policy.getRecipe());
324         operation.clOperation.setTarget(this.policy.getTarget().toString());
325         operation.clOperation.setSubRequestId(Integer.toString(operation.attempt));
326         //
327         // Now determine which actor we need to construct a request for
328         //
329         try {
330             switch (policy.getActor()) {
331                 case "APPC":
332                     return startAppcOperation(onset, operation);
333                 case "SO":
334                     return startSoOperation(onset, operation);
335                 case "VFC":
336                     return startVfcOperation(onset, operation);
337                 case "SDNR":
338                     return startSdnrOperation(onset, operation);
339                 case "SDNC":
340                     return startSdncOperation(onset, operation);
341                 case "CDS":
342                     return startCdsOperation(onset, operation);
343                 default:
344                     throw new ControlLoopException(
345                         "invalid actor " + policy.getActor() + " on policy");
346             }
347
348         } catch (AaiException e) {
349             throw new ControlLoopException(e.getMessage(), e);
350         }
351     }
352
353     private Object startAppcOperation(ControlLoopEvent onset, Operation operation) {
354         /*
355          * If the recipe is ModifyConfig, a legacy APPC request is constructed. Otherwise an
356          * LCMRequest is
357          * constructed.
358          */
359         this.currentOperation = operation;
360         if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) {
361             this.operationRequest =
362                 AppcActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset,
363                     operation.clOperation, this.policy, this.targetEntity);
364         } else {
365             this.operationRequest =
366                 AppcLcmActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset,
367                     operation.clOperation, this.policy, this.targetEntity);
368         }
369         //
370         // Save the operation
371         //
372
373         return operationRequest;
374     }
375
376     private Object startSoOperation(ControlLoopEvent onset, Operation operation) {
377         SoActorServiceProvider soActorSp = new SoActorServiceProvider();
378         this.operationRequest = soActorSp.constructRequestCq((VirtualControlLoopEvent) onset,
379             operation.clOperation, this.policy, this.aaiCqResponse);
380
381         // Save the operation
382         this.currentOperation = operation;
383
384         if (this.operationRequest == null) {
385             this.policyResult = PolicyResult.FAILURE;
386         }
387
388         return operationRequest;
389     }
390
391     private Object startVfcOperation(ControlLoopEvent onset, Operation operation) {
392         this.operationRequest =
393             VfcActorServiceProvider.constructRequestCq((VirtualControlLoopEvent) onset,
394                 operation.clOperation, this.policy, this.aaiCqResponse);
395         this.currentOperation = operation;
396         if (this.operationRequest == null) {
397             this.policyResult = PolicyResult.FAILURE;
398         }
399         return operationRequest;
400     }
401
402     private Object startSdnrOperation(ControlLoopEvent onset, Operation operation) {
403         /*
404          * If the recipe is ModifyConfig or ModifyConfigANR, a SDNR request is constructed.
405          */
406         this.currentOperation = operation;
407         this.operationRequest = SdnrActorServiceProvider
408             .constructRequest((VirtualControlLoopEvent) onset, operation.clOperation, this.policy);
409         //
410         // Save the operation
411         //
412         if (this.operationRequest == null) {
413             this.policyResult = PolicyResult.FAILURE;
414         }
415
416         return operationRequest;
417     }
418
419     private Object startSdncOperation(ControlLoopEvent onset, Operation operation) {
420         SdncActorServiceProvider provider = new SdncActorServiceProvider();
421         this.operationRequest = provider.constructRequest((VirtualControlLoopEvent) onset,
422             operation.clOperation, this.policy);
423         this.currentOperation = operation;
424         if (this.operationRequest == null) {
425             this.policyResult = PolicyResult.FAILURE;
426         }
427         return operationRequest;
428     }
429
430     private Object startCdsOperation(ControlLoopEvent onset, Operation operation)
431         throws AaiException {
432
433         CdsActorServiceProvider provider = new CdsActorServiceProvider();
434         Optional<ExecutionServiceInput> optionalRequest =
435             provider.constructRequest((VirtualControlLoopEvent) onset, operation.clOperation,
436                 this.policy, this.buildAaiParams());
437
438         this.currentOperation = operation;
439         if (optionalRequest.isPresent()) {
440             this.operationRequest = optionalRequest.get();
441         } else {
442             this.operationRequest = null;
443             this.policyResult = PolicyResult.FAILURE;
444         }
445
446         return this.operationRequest;
447     }
448
449     /**
450      * Build AAI parameters for CDS operation.
451      *
452      * @return a map containing vnf id key and value for the vnf to apply the action to.
453      * @throws AaiException if the vnf can not be found.
454      */
455     private Map<String, String> buildAaiParams() throws AaiException {
456
457         Map<String, String> result = new HashMap<>();
458
459         if (TargetType.VNF.equals(policy.getTarget().getType())
460             || TargetType.VFMODULE.equals(policy.getTarget().getType())) {
461
462             ServiceInstance serviceInstance = this.aaiCqResponse.getServiceInstance();
463             if (serviceInstance == null) {
464                 logger.info("Target entity service instance could not be found");
465                 throw new AaiException("Target service instance could not be found");
466             }
467
468             GenericVnf genericVnf = this.aaiCqResponse
469                 .getGenericVnfByModelInvariantId(policy.getTarget().getResourceID());
470             if (genericVnf == null) {
471                 logger.info("Target entity generic vnf could not be found");
472                 throw new AaiException("Target generic vnf could not be found");
473             }
474
475             result.put(AAI_SERVICE_INSTANCE_ID_KEY, serviceInstance.getServiceInstanceId());
476             result.put(GENERIC_VNF_VNF_ID, genericVnf.getVnfId());
477
478         } else if (TargetType.PNF.equals(policy.getTarget().getType())) {
479             result = this.eventManager.getPnf((VirtualControlLoopEvent) onset);
480         }
481
482         return result;
483
484     }
485
486     /**
487      * Handle a response.
488      *
489      * @param response the response
490      * @return a PolicyResult
491      */
492     public PolicyResult onResponse(Object response) {
493         //
494         // Which response is it?
495         //
496         if (response instanceof Response) {
497             //
498             // Cast APPC response and handle it
499             //
500             return onResponse((Response) response);
501         } else if (response instanceof AppcLcmDmaapWrapper) {
502             //
503             // Cast LCM response and handle it
504             //
505             return onResponse((AppcLcmDmaapWrapper) response);
506         } else if (response instanceof PciResponseWrapper) {
507             //
508             // Cast SDNR response and handle it
509             //
510             return onResponse((PciResponseWrapper) response);
511         } else if (response instanceof SoResponseWrapper) {
512             //
513             // Cast SO response and handle it
514             //
515             return onResponse((SoResponseWrapper) response);
516         } else if (response instanceof VfcResponse) {
517             //
518             // Cast VFC response and handle it
519             //
520             return onResponse((VfcResponse) response);
521         } else if (response instanceof SdncResponse) {
522             //
523             // Cast SDNC response and handle it
524             //
525             return onResponse((SdncResponse) response);
526         } else if (response instanceof CdsResponse) {
527             //
528             // Cast CDS response and handle it
529             //
530             return onResponse((CdsResponse) response);
531         } else {
532             return null;
533         }
534     }
535
536     /**
537      * This method handles operation responses from APPC.
538      *
539      * @param appcResponse the APPC response
540      * @return The result of the response handling
541      */
542     private PolicyResult onResponse(Response appcResponse) {
543         //
544         // Determine which subrequestID (ie. attempt)
545         //
546         Integer operationAttempt = getSubRequestId(appcResponse);
547         if (operationAttempt == null) {
548             this.completeOperation(operationAttempt,
549                 "Policy was unable to parse APP-C SubRequestID (it was null).",
550                 PolicyResult.FAILURE_EXCEPTION);
551             return PolicyResult.FAILURE_EXCEPTION;
552         }
553         //
554         // Sanity check the response message
555         //
556         if (appcResponse.getStatus() == null) {
557             //
558             // We cannot tell what happened if this doesn't exist
559             //
560             this.completeOperation(operationAttempt,
561                 "Policy was unable to parse APP-C response status field (it was null).",
562                 PolicyResult.FAILURE_EXCEPTION);
563             return PolicyResult.FAILURE_EXCEPTION;
564         }
565         //
566         // Get the Response Code
567         //
568         ResponseCode code = ResponseCode.toResponseCode(appcResponse.getStatus().getCode());
569         if (code == null) {
570             //
571             // We are unaware of this code
572             //
573             this.completeOperation(operationAttempt,
574                 "Policy was unable to parse APP-C response status code field.",
575                 PolicyResult.FAILURE_EXCEPTION);
576             return PolicyResult.FAILURE_EXCEPTION;
577         }
578
579         return onResponse(appcResponse, operationAttempt, code);
580     }
581
582     private PolicyResult onResponse(Response appcResponse, Integer operationAttempt,
583         ResponseCode code) {
584         //
585         // Ok, let's figure out what APP-C's response is
586         //
587         switch (code) {
588             case ACCEPT:
589                 //
590                 // This is good, they got our original message and
591                 // acknowledged it.
592                 //
593                 // Is there any need to track this?
594                 //
595                 return null;
596             case ERROR:
597             case REJECT:
598                 //
599                 // We'll consider these two codes as exceptions
600                 //
601                 this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(),
602                     PolicyResult.FAILURE_EXCEPTION);
603                 return getTimeoutResult(PolicyResult.FAILURE_EXCEPTION);
604             case SUCCESS:
605                 //
606                 //
607                 //
608                 this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(),
609                     PolicyResult.SUCCESS);
610                 return getTimeoutResult(PolicyResult.SUCCESS);
611             case FAILURE:
612                 //
613                 //
614                 //
615                 this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(),
616                     PolicyResult.FAILURE);
617                 return getTimeoutResult(PolicyResult.FAILURE);
618             default:
619                 return null;
620         }
621     }
622
623     /**
624      * This method handles operation responses from LCM.
625      *
626      * @param dmaapResponse the LCM response
627      * @return The result of the response handling
628      */
629     private PolicyResult onResponse(AppcLcmDmaapWrapper dmaapResponse) {
630         /*
631          * Parse out the operation attempt using the subrequestid
632          */
633         Integer operationAttempt = AppcLcmActorServiceProvider.parseOperationAttempt(
634             dmaapResponse.getBody().getOutput().getCommonHeader().getSubRequestId());
635         if (operationAttempt == null) {
636             this.completeOperation(operationAttempt,
637                 "Policy was unable to parse APP-C SubRequestID (it was null).",
638                 PolicyResult.FAILURE_EXCEPTION);
639             return PolicyResult.FAILURE_EXCEPTION;
640         }
641
642         /*
643          * Process the APPCLCM response to see what PolicyResult should be returned
644          */
645         AbstractMap.SimpleEntry<PolicyResult, String> result =
646             AppcLcmActorServiceProvider.processResponse(dmaapResponse);
647
648         if (result.getKey() != null) {
649             this.completeOperation(operationAttempt, result.getValue(), result.getKey());
650             if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
651                 return null;
652             }
653             return result.getKey();
654         }
655         return null;
656     }
657
658     /**
659      * This method handles operation responses from SDNR.
660      *
661      * @param dmaapResponse the SDNR response
662      * @return the result of the response handling
663      */
664     private PolicyResult onResponse(PciResponseWrapper dmaapResponse) {
665         /*
666          * Parse out the operation attempt using the subrequestid
667          */
668         Integer operationAttempt = SdnrActorServiceProvider
669             .parseOperationAttempt(dmaapResponse.getBody().getCommonHeader().getSubRequestId());
670         if (operationAttempt == null) {
671             this.completeOperation(operationAttempt,
672                 "Policy was unable to parse SDNR SubRequestID.", PolicyResult.FAILURE_EXCEPTION);
673             return PolicyResult.FAILURE_EXCEPTION;
674         }
675
676         /*
677          * Process the SDNR response to see what PolicyResult should be returned
678          */
679         Pair<PolicyResult, String> result =
680             SdnrActorServiceProvider.processResponse(dmaapResponse);
681
682         if (result.getLeft() != null) {
683             this.completeOperation(operationAttempt, result.getRight(), result.getLeft());
684             if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
685                 return null;
686             }
687             return result.getLeft();
688         }
689         return null;
690     }
691
692     /**
693      * This method handles operation responses from SO.
694      *
695      * @param msoResponse the SO response
696      * @return The result of the response handling
697      */
698     private PolicyResult onResponse(SoResponseWrapper msoResponse) {
699         switch (msoResponse.getSoResponse().getHttpResponseCode()) {
700             case 200:
701             case 202:
702                 //
703                 // Consider it as success
704                 //
705                 this.completeOperation(this.attempts,
706                     msoResponse.getSoResponse().getHttpResponseCode() + SUCCESS_MSG,
707                     PolicyResult.SUCCESS);
708                 return getTimeoutResult(PolicyResult.SUCCESS);
709             default:
710                 //
711                 // Consider it as failure
712                 //
713                 this.completeOperation(this.attempts,
714                     msoResponse.getSoResponse().getHttpResponseCode() + FAILED_MSG,
715                     PolicyResult.FAILURE);
716                 return getTimeoutResult(PolicyResult.FAILURE);
717         }
718     }
719
720     /**
721      * This method handles operation responses from VFC.
722      *
723      * @param vfcResponse the VFC response
724      * @return The result of the response handling
725      */
726     private PolicyResult onResponse(VfcResponse vfcResponse) {
727         if ("finished".equalsIgnoreCase(vfcResponse.getResponseDescriptor().getStatus())) {
728             //
729             // Consider it as success
730             //
731             this.completeOperation(this.attempts, SUCCESS_MSG, PolicyResult.SUCCESS);
732             return getTimeoutResult(PolicyResult.SUCCESS);
733         } else {
734             //
735             // Consider it as failure
736             //
737             this.completeOperation(this.attempts, FAILED_MSG, PolicyResult.FAILURE);
738             if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
739                 return null;
740             }
741             // increment operation attempts for retries
742             this.attempts += 1;
743             return PolicyResult.FAILURE;
744         }
745     }
746
747     /**
748      * This method handles operation responses from SDNC.
749      *
750      * @param sdncResponse the VFC response
751      * @return The result of the response handling
752      */
753     private PolicyResult onResponse(SdncResponse sdncResponse) {
754         if ("200".equals(sdncResponse.getResponseOutput().getResponseCode())) {
755             //
756             // Consider it as success
757             //
758             this.completeOperation(this.attempts, SUCCESS_MSG, PolicyResult.SUCCESS);
759             return getTimeoutResult(PolicyResult.SUCCESS);
760         } else {
761             //
762             // Consider it as failure
763             //
764             this.completeOperation(this.attempts, FAILED_MSG, PolicyResult.FAILURE);
765             if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
766                 return null;
767             }
768             // increment operation attempts for retries
769             this.attempts += 1;
770             return PolicyResult.FAILURE;
771         }
772     }
773
774     /**
775      * This method handles operation responses from CDS.
776      *
777      * @param response the CDS response
778      * @return The result of the response handling
779      */
780     private PolicyResult onResponse(CdsResponse response) {
781         if (response != null && CdsActorConstants.SUCCESS.equals(response.getStatus())) {
782             //
783             // Consider it as success
784             //
785             this.completeOperation(this.attempts, SUCCESS_MSG, PolicyResult.SUCCESS);
786             return getTimeoutResult(PolicyResult.SUCCESS);
787         } else {
788             //
789             // Consider it as failure
790             //
791             this.completeOperation(this.attempts, FAILED_MSG, PolicyResult.FAILURE);
792             if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
793                 return null;
794             }
795             // increment operation attempts for retries
796             this.attempts += 1;
797             return PolicyResult.FAILURE;
798         }
799     }
800
801     private PolicyResult getTimeoutResult(PolicyResult result) {
802         return (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult) ? null : result);
803     }
804
805     private Integer getSubRequestId(Response appcResponse) {
806         try {
807             return Integer.valueOf(appcResponse.getCommonHeader().getSubRequestId());
808         } catch (NumberFormatException e) {
809             //
810             // We cannot tell what happened if this doesn't exist
811             //
812             return null;
813         }
814     }
815
816     /**
817      * Get the operation timeout.
818      *
819      * @return the timeout
820      */
821     public Integer getOperationTimeout() {
822         //
823         // Sanity check
824         //
825         if (this.policy == null) {
826             logger.debug("getOperationTimeout returning 0");
827             return 0;
828         }
829         logger.debug("getOperationTimeout returning {}", this.policy.getTimeout());
830         return this.policy.getTimeout();
831     }
832
833     /**
834      * Get the operation timeout as a String.
835      *
836      * @param defaultTimeout the default timeout
837      * @return the timeout as a String
838      */
839     public String getOperationTimeoutString(int defaultTimeout) {
840         Integer to = this.getOperationTimeout();
841         if (to == null || to == 0) {
842             return Integer.toString(defaultTimeout) + "s";
843         }
844         return to.toString() + "s";
845     }
846
847     public PolicyResult getOperationResult() {
848         return this.policyResult;
849     }
850
851     /**
852      * Get the operation as a message.
853      *
854      * @return the operation as a message
855      */
856     public String getOperationMessage() {
857         if (this.currentOperation != null && this.currentOperation.clOperation != null) {
858             return this.currentOperation.clOperation.toMessage();
859         }
860
861         if (!this.operationHistory.isEmpty()) {
862             return this.operationHistory.getLast().clOperation.toMessage();
863         }
864         return null;
865     }
866
867     /**
868      * Get the operation as a message including the guard result.
869      *
870      * @param guardResult the guard result
871      * @return the operation as a message including the guard result
872      */
873     public String getOperationMessage(String guardResult) {
874         if (this.currentOperation != null && this.currentOperation.clOperation != null) {
875             return this.currentOperation.clOperation.toMessage() + ", Guard result: " + guardResult;
876         }
877
878         if (!this.operationHistory.isEmpty()) {
879             return this.operationHistory.getLast().clOperation.toMessage() + ", Guard result: "
880                 + guardResult;
881         }
882         return null;
883     }
884
885     /**
886      * Get the operation history.
887      *
888      * @return the operation history
889      */
890     public String getOperationHistory() {
891         if (this.currentOperation != null && this.currentOperation.clOperation != null) {
892             return this.currentOperation.clOperation.toHistory();
893         }
894
895         if (!this.operationHistory.isEmpty()) {
896             return this.operationHistory.getLast().clOperation.toHistory();
897         }
898         return null;
899     }
900
901     /**
902      * Get the history.
903      *
904      * @return the list of control loop operations
905      */
906     public List<ControlLoopOperation> getHistory() {
907         LinkedList<ControlLoopOperation> history = new LinkedList<>();
908         for (Operation op : this.operationHistory) {
909             history.add(new ControlLoopOperation(op.clOperation));
910
911         }
912         return history;
913     }
914
915     /**
916      * Set the operation has timed out.
917      */
918     public void setOperationHasTimedOut() {
919         //
920         //
921         //
922         this.completeOperation(this.attempts, "Operation timed out", PolicyResult.FAILURE_TIMEOUT);
923     }
924
925     /**
926      * Set the operation has been denied by guard.
927      */
928     public void setOperationHasGuardDeny() {
929         //
930         //
931         //
932         this.completeOperation(this.attempts, "Operation denied by Guard",
933             PolicyResult.FAILURE_GUARD);
934     }
935
936     public void setOperationHasException(String message) {
937         this.completeOperation(this.attempts, message, PolicyResult.FAILURE_EXCEPTION);
938     }
939
940     /**
941      * Is the operation complete.
942      *
943      * @return <code>true</code> if the operation is complete, <code>false</code> otherwise
944      */
945     public boolean isOperationComplete() {
946         //
947         // Is there currently a result?
948         //
949         if (this.policyResult == null) {
950             //
951             // either we are in process or we
952             // haven't started
953             //
954             return false;
955         }
956         //
957         // We have some result, check if the operation failed
958         //
959         if (this.policyResult.equals(PolicyResult.FAILURE)) {
960             //
961             // Check if there were no retries specified
962             //
963             if (getMaxRetries() < 1) {
964                 //
965                 // The result is the failure
966                 //
967                 return true;
968             }
969             //
970             // Check retries
971             //
972             if (this.attempts > getMaxRetries()) {
973                 //
974                 // No more attempts allowed, reset
975                 // that our actual result is failure due to retries
976                 //
977                 this.policyResult = PolicyResult.FAILURE_RETRIES;
978                 return true;
979             } else {
980                 //
981                 // There are more attempts available to try the
982                 // policy recipe.
983                 //
984                 return false;
985             }
986         }
987         //
988         // Other results mean we are done
989         //
990         return true;
991     }
992
993     public boolean isOperationRunning() {
994         return (this.currentOperation != null);
995     }
996
997     /**
998      * This method verifies that the operation manager may run an operation.
999      *
1000      * @return True if the operation can run, false otherwise
1001      * @throws ControlLoopException if the operation cannot run
1002      */
1003     private void verifyOperatonCanRun() throws ControlLoopException {
1004         //
1005         // They shouldn't call us if we currently running something
1006         //
1007         if (this.currentOperation != null) {
1008             //
1009             // what do we do if we are already running an operation?
1010             //
1011             throw new ControlLoopException(
1012                 "current operation is not null (an operation is already running)");
1013         }
1014         //
1015         // Check if we have maxed out on retries
1016         //
1017         if (getMaxRetries() < 1) {
1018             //
1019             // No retries are allowed, so check have we even made
1020             // one attempt to execute the operation?
1021             //
1022             if (this.attempts >= 1) {
1023                 //
1024                 // We have, let's ensure our PolicyResult is set
1025                 //
1026                 if (this.policyResult == null) {
1027                     this.policyResult = PolicyResult.FAILURE_RETRIES;
1028                 }
1029                 //
1030                 //
1031                 //
1032                 throw new ControlLoopException(
1033                     "current operation failed and retries are not allowed");
1034             }
1035         } else {
1036             //
1037             // Have we maxed out on retries?
1038             //
1039             if (this.attempts > getMaxRetries()) {
1040                 if (this.policyResult == null) {
1041                     this.policyResult = PolicyResult.FAILURE_RETRIES;
1042                 }
1043                 throw new ControlLoopException(
1044                     "current oepration has failed after " + this.attempts + " retries");
1045             }
1046         }
1047     }
1048
1049     /**
1050      * Gets the maximum number of retries.
1051      *
1052      * @return the maximum number of retries, or {@code 0}, if not specified
1053      */
1054     public int getMaxRetries() {
1055         return (policy.getRetry() != null ? policy.getRetry() : 0);
1056     }
1057
1058     private void storeOperationInDataBase() {
1059         // Only store in DB if enabled
1060         boolean guardEnabled = "false".equalsIgnoreCase(
1061             PolicyEngineConstants.getManager().getEnvironmentProperty("guard.disabled"));
1062         if (!guardEnabled) {
1063             return;
1064         }
1065
1066         // DB Properties
1067         Properties props = new Properties();
1068         if (PolicyEngineConstants.getManager().getEnvironmentProperty(Util.ONAP_KEY_URL) != null
1069             && PolicyEngineConstants.getManager().getEnvironmentProperty(Util.ONAP_KEY_USER) != null
1070             && PolicyEngineConstants.getManager()
1071                 .getEnvironmentProperty(Util.ONAP_KEY_PASS) != null) {
1072             props.put(Util.ECLIPSE_LINK_KEY_URL,
1073                 PolicyEngineConstants.getManager().getEnvironmentProperty(Util.ONAP_KEY_URL));
1074             props.put(Util.ECLIPSE_LINK_KEY_USER,
1075                 PolicyEngineConstants.getManager().getEnvironmentProperty(Util.ONAP_KEY_USER));
1076             props.put(Util.ECLIPSE_LINK_KEY_PASS,
1077                 PolicyEngineConstants.getManager().getEnvironmentProperty(Util.ONAP_KEY_PASS));
1078             props.put(PersistenceUnitProperties.CLASSLOADER,
1079                 ControlLoopOperationManager.class.getClassLoader());
1080         }
1081
1082         String opsHistPu = System.getProperty("OperationsHistoryPU");
1083         if (!"OperationsHistoryPUTest".equals(opsHistPu)) {
1084             opsHistPu = "OperationsHistoryPU";
1085         } else {
1086             props.clear();
1087         }
1088         EntityManager em;
1089         try {
1090             em = Persistence.createEntityManagerFactory(opsHistPu, props).createEntityManager();
1091         } catch (Exception e) {
1092             logger.error("storeOperationInDataBase threw: ", e);
1093             return;
1094         }
1095
1096         Dbao newEntry = new Dbao();
1097
1098         newEntry.setClosedLoopName(this.onset.getClosedLoopControlName());
1099         newEntry.setRequestId(this.onset.getRequestId().toString());
1100         newEntry.setActor(this.currentOperation.clOperation.getActor());
1101         newEntry.setOperation(this.currentOperation.clOperation.getOperation());
1102         newEntry.setTarget(this.targetEntity);
1103         newEntry.setStarttime(Timestamp.from(this.currentOperation.clOperation.getStart()));
1104         newEntry.setSubrequestId(this.currentOperation.clOperation.getSubRequestId());
1105         newEntry
1106             .setEndtime(new Timestamp(this.currentOperation.clOperation.getEnd().toEpochMilli()));
1107         newEntry.setMessage(this.currentOperation.clOperation.getMessage());
1108         newEntry.setOutcome(this.currentOperation.clOperation.getOutcome());
1109
1110         em.getTransaction().begin();
1111         em.persist(newEntry);
1112         em.getTransaction().commit();
1113
1114         em.close();
1115     }
1116
1117     private void completeOperation(Integer attempt, String message, PolicyResult result) {
1118         if (attempt == null) {
1119             logger.debug("attempt cannot be null (i.e. subRequestID)");
1120             return;
1121         }
1122         if (this.currentOperation != null) {
1123             if (this.currentOperation.attempt == attempt.intValue()) {
1124                 this.currentOperation.clOperation.setEnd(Instant.now());
1125                 this.currentOperation.clOperation.setMessage(message);
1126                 this.currentOperation.clOperation.setOutcome(result.toString());
1127                 this.currentOperation.policyResult = result;
1128                 //
1129                 // Save it in history
1130                 //
1131                 this.operationHistory.add(this.currentOperation);
1132                 this.storeOperationInDataBase();
1133                 //
1134                 // Set our last result
1135                 //
1136                 this.policyResult = result;
1137                 //
1138                 // Clear the current operation field
1139                 //
1140                 this.currentOperation = null;
1141                 return;
1142             }
1143             logger.debug("not current");
1144         }
1145         for (Operation op : this.operationHistory) {
1146             if (op.attempt == attempt.intValue()) {
1147                 op.clOperation.setEnd(Instant.now());
1148                 op.clOperation.setMessage(message);
1149                 op.clOperation.setOutcome(result.toString());
1150                 op.policyResult = result;
1151                 return;
1152             }
1153         }
1154         logger.debug("Could not find associated operation");
1155     }
1156
1157     /**
1158      * Commit the abatement to the history database.
1159      *
1160      * @param message the abatement message
1161      * @param outcome the abatement outcome
1162      */
1163     public void commitAbatement(String message, String outcome) {
1164         logger.info("commitAbatement: {}. {}", message, outcome);
1165
1166         if (this.currentOperation == null) {
1167             try {
1168                 this.currentOperation = this.operationHistory.getLast();
1169             } catch (NoSuchElementException e) {
1170                 logger.error("{}: commitAbatement threw an exception ", this, e);
1171                 return;
1172             }
1173         }
1174         this.currentOperation.clOperation.setEnd(Instant.now());
1175         this.currentOperation.clOperation.setMessage(message);
1176         this.currentOperation.clOperation.setOutcome(outcome);
1177         //
1178         // Store commit in DB
1179         //
1180         this.storeOperationInDataBase();
1181         //
1182         // Clear the current operation field
1183         //
1184         this.currentOperation = null;
1185     }
1186
1187     /**
1188      * Construct a ControlLoopResponse object from actor response and input event.
1189      *
1190      * @param response the response from actor
1191      * @param event the input event
1192      *
1193      * @return a ControlLoopResponse
1194      */
1195     public ControlLoopResponse getControlLoopResponse(Object response,
1196         VirtualControlLoopEvent event) {
1197         if (response instanceof PciResponseWrapper) {
1198             //
1199             // Cast SDNR response and handle it
1200             //
1201             return SdnrActorServiceProvider.getControlLoopResponse((PciResponseWrapper) response,
1202                 event);
1203         } else {
1204             return null;
1205         }
1206     }
1207
1208 }