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