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