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