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