1c46b999118d9bf6bf2596be81da99ae8e245502
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * controlloop operation manager
4  * ================================================================================
5  * Copyright (C) 2017 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.Properties;
29
30 import javax.persistence.EntityManager;
31 import javax.persistence.Persistence;
32
33 import org.onap.policy.aai.util.AAIException;
34 import org.onap.policy.appc.Response;
35 import org.onap.policy.appc.ResponseCode;
36 import org.onap.policy.appclcm.LCMResponseWrapper;
37 import org.onap.policy.controlloop.ControlLoopEvent;
38 import org.onap.policy.controlloop.ControlLoopException;
39 import org.onap.policy.controlloop.ControlLoopOperation;
40 import org.onap.policy.controlloop.VirtualControlLoopEvent;
41 import org.onap.policy.controlloop.actor.appc.APPCActorServiceProvider;
42 import org.onap.policy.controlloop.actor.appclcm.AppcLcmActorServiceProvider;
43 import org.onap.policy.controlloop.actor.so.SOActorServiceProvider;
44 import org.onap.policy.controlloop.actor.vfc.VFCActorServiceProvider;
45 import org.onap.policy.controlloop.policy.Policy;
46 import org.onap.policy.controlloop.policy.PolicyResult;
47 import org.onap.policy.drools.system.PolicyEngine;
48 import org.onap.policy.guard.Util;
49 import org.onap.policy.so.SOResponse;
50 import org.onap.policy.vfc.VFCResponse;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 public class ControlLoopOperationManager implements Serializable {
55
56         /**
57          *
58          */
59         private static final long serialVersionUID = -3773199283624595410L;
60         private static final Logger logger = LoggerFactory.getLogger(ControlLoopOperationManager.class);
61
62         @Override
63         public String toString() {
64                 return "ControlLoopOperationManager [onset=" + (onset != null ? onset.requestID : "null") + ", policy="
65                                 + (policy != null ? policy.getId() : "null") + ", attempts=" + attempts
66                                 + ", policyResult=" + policyResult
67                                 + ", currentOperation=" + currentOperation + ", operationHistory=" + operationHistory
68                                 + "]";
69         }
70
71         //
72         // These properties are not changeable, but accessible
73         // for Drools Rule statements.
74         //
75         //public final ATTControlLoopEvent onset;
76         public final ControlLoopEvent onset;
77         public final transient Policy policy;
78
79         //
80         // Properties used to track the Operation
81         //
82         private int attempts = 0;
83         private transient Operation currentOperation = null;
84         private LinkedList<Operation> operationHistory = new LinkedList<Operation>();
85         private PolicyResult policyResult = null;
86         private ControlLoopEventManager eventManager = null;
87         private String targetEntity;
88
89         public ControlLoopEventManager getEventManager() {
90                 return eventManager;
91         }
92
93         public void setEventManager(ControlLoopEventManager eventManager) {
94                 this.eventManager = eventManager;
95         }
96
97         public String getTargetEntity() {
98             return this.targetEntity;
99         }
100
101         //
102         // Internal class used for tracking
103         //
104         private class Operation {
105                 public ControlLoopOperation operation = new ControlLoopOperation();
106                 public PolicyResult policyResult = null;
107                 public int attempt = 0;
108
109                 @Override
110                 public String toString() {
111                         return "Operation [attempt=" + attempt + ", policyResult=" + policyResult + ", operation=" + operation
112                                         + "]";
113                 }
114         }
115
116         private String guardApprovalStatus = "NONE";//"NONE", "PERMIT", "DENY"
117         private transient Object operationRequest;
118
119         public Object getOperationRequest() {
120                 return operationRequest;
121         }
122
123         public String getGuardApprovalStatus() {
124                 return guardApprovalStatus;
125         }
126         public void setGuardApprovalStatus(String guardApprovalStatus) {
127                 this.guardApprovalStatus = guardApprovalStatus;
128         }
129
130         public String getTarget(Policy policy) throws ControlLoopException, AAIException {
131         if (policy.getTarget() != null) {
132             if (policy.getTarget().getType() != null) {
133                 switch(policy.getTarget().getType()) {
134                 case PNF:
135                     break;
136                 case VM:
137                 case VNF:
138                     VirtualControlLoopEvent virtualOnset = (VirtualControlLoopEvent) this.onset;
139                     if (this.onset.target.equalsIgnoreCase("vserver.vserver-name")) {
140                         return virtualOnset.AAI.get("vserver.vserver-name");
141                     }
142                     else if (this.onset.target.equalsIgnoreCase("generic-vnf.vnf-id")) {
143                         return virtualOnset.AAI.get("generic-vnf.vnf-id");
144                     }
145                     else if (this.onset.target.equalsIgnoreCase("generic-vnf.vnf-name")) {
146                         /*
147                          * If the vnf-name was retrieved from the onset then the vnf-id
148                          * must be obtained from the event manager's A&AI GET query
149                          */
150                         String vnfId = this.eventManager.getVnfResponse().vnfID;
151                         if (vnfId == null) {
152                             throw new AAIException("No vnf-id found");
153                         }
154                         return vnfId;
155                     }
156                     break;
157                 default:
158                     throw new ControlLoopException("The target type is not supported");
159                 }
160             }
161             else {
162                 throw new ControlLoopException("The target type is null");
163             }
164         }
165         else {
166             throw new ControlLoopException("The target is null");
167         }
168         return null;
169     }
170         
171         public ControlLoopOperationManager(ControlLoopEvent onset, Policy policy, ControlLoopEventManager em) throws ControlLoopException, AAIException {
172                 this.onset = onset;
173                 this.policy = policy;
174                 this.guardApprovalStatus = "NONE";
175                 this.eventManager = em;
176                 this.targetEntity = getTarget(policy);
177                 
178                 //
179                 // Let's make a sanity check
180                 //
181                 switch (policy.getActor()) {
182                 case "APPC":
183                     if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) {
184                         /*
185                  * The target vnf-id may not be the same as the source vnf-id
186                  * specified in the yaml, the target vnf-id is retrieved by
187                  * a named query to A&AI.
188                  */
189                         String targetVnf = AppcLcmActorServiceProvider.vnfNamedQuery(
190                                     policy.getTarget().getResourceID(), this.targetEntity);
191                         this.targetEntity = targetVnf;
192                     }
193                         break;
194                 case "SO":
195                     break;
196                 case "VFC":
197                         break;
198                 default:
199                         throw new ControlLoopException("ControlLoopEventManager: policy has an unknown actor.");
200                 }
201         }
202
203         public Object startOperation(/*VirtualControlLoopEvent*/ControlLoopEvent onset) throws AAIException {
204                 //
205                 // They shouldn't call us if we currently running something
206                 //
207                 if (this.currentOperation != null) {
208                         //
209                         // what do we do if we are already running an operation?
210                         //
211                         return null;
212                 }
213                 //
214                 // Check if we have maxed out on retries
215                 //
216                 if (this.policy.getRetry() == null || this.policy.getRetry() < 1) {
217                         //
218                         // No retries are allowed, so check have we even made
219                         // one attempt to execute the operation?
220                         //
221                         if (this.attempts >= 1) {
222                                 //
223                                 // We have, let's ensure our PolicyResult is set
224                                 //
225                                 if (this.policyResult == null) {
226                                         this.policyResult = PolicyResult.FAILURE_RETRIES;
227                                 }
228                                 //
229                                 //
230                                 //
231                                 return null;
232                         }
233                 } else {
234                         //
235                         // Have we maxed out on retries?
236                         //
237                         if (this.attempts > this.policy.getRetry()) {
238                                 if (this.policyResult == null) {
239                                         this.policyResult = PolicyResult.FAILURE_RETRIES;
240                                 }
241                                 return null;
242                         }
243                 }
244                 //
245                 // Setup
246                 //
247                 this.policyResult = null;
248                 Operation operation = new Operation();
249                 operation.attempt = ++this.attempts;
250                 operation.operation.actor = this.policy.getActor();
251                 operation.operation.operation = this.policy.getRecipe();
252                 operation.operation.target = this.policy.getTarget().toString();
253                 operation.operation.subRequestId = Integer.toString(operation.attempt);
254                 //
255                 // Now determine which actor we need to construct a request for
256                 //
257                 switch (policy.getActor()) {
258                 case "APPC":
259                     /*
260                      * If the recipe is ModifyConfig, a legacy APPC
261                      * request is constructed. Otherwise an LCMRequest
262                      * is constructed.
263                      */
264                         this.currentOperation = operation;
265                     if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) {
266
267                     this.operationRequest = APPCActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, 
268                                             operation.operation, this.policy, this.targetEntity);
269                     }
270                     else {
271                         this.operationRequest = AppcLcmActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, 
272                                                 operation.operation, this.policy, this.targetEntity);
273                     }
274                         //
275                         // Save the operation
276                         //
277                         
278                         return operationRequest;
279                 case "SO":
280                         SOActorServiceProvider SOAsp = new SOActorServiceProvider();
281                         this.operationRequest = SOAsp.constructRequest((VirtualControlLoopEvent)onset, operation.operation, this.policy);
282
283                         // Save the operation
284                         this.currentOperation = operation;
285
286                         if (this.operationRequest == null) {
287                                 this.policyResult = PolicyResult.FAILURE;
288                         }
289
290                         return operationRequest;
291                 case "VFC":
292                         this.operationRequest = VFCActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, operation.operation, this.policy, this.eventManager.getVnfResponse());
293                         this.currentOperation = operation;
294                         return operationRequest;
295
296                 }
297                 return null;
298         }
299
300         public PolicyResult     onResponse(Object response) {
301                 //
302                 // Which response is it?
303                 //
304                 if (response instanceof Response) {
305                         //
306                         // Cast it
307                         //
308                         Response appcResponse = (Response) response;
309                         //
310                         // Determine which subrequestID (ie. attempt)
311                         //
312                         Integer operationAttempt = null;
313                         try {
314                                 operationAttempt = Integer.parseInt(appcResponse.CommonHeader.SubRequestID);
315                         } catch (NumberFormatException e) {
316                                 //
317                                 // We cannot tell what happened if this doesn't exist
318                                 //
319                                 this.completeOperation(operationAttempt, "Policy was unable to parse APP-C SubRequestID (it was null).", PolicyResult.FAILURE_EXCEPTION);
320                                 return PolicyResult.FAILURE_EXCEPTION;
321                         }
322                         //
323                         // Sanity check the response message
324                         //
325                         if (appcResponse.Status == null) {
326                                 //
327                                 // We cannot tell what happened if this doesn't exist
328                                 //
329                                 this.completeOperation(operationAttempt, "Policy was unable to parse APP-C response status field (it was null).", PolicyResult.FAILURE_EXCEPTION);
330                                 return PolicyResult.FAILURE_EXCEPTION;
331                         }
332                         //
333                         // Get the Response Code
334                         //
335                         ResponseCode code = ResponseCode.toResponseCode(appcResponse.Status.Code);
336                         if (code == null) {
337                                 //
338                                 // We are unaware of this code
339                                 //
340                                 this.completeOperation(operationAttempt, "Policy was unable to parse APP-C response status code field.", PolicyResult.FAILURE_EXCEPTION);
341                                 return PolicyResult.FAILURE_EXCEPTION;
342                         }
343                         //
344                         // Ok, let's figure out what APP-C's response is
345                         //
346                         switch (code) {
347                         case ACCEPT:
348                                 //
349                                 // This is good, they got our original message and
350                                 // acknowledged it.
351                                 //
352                                 // Is there any need to track this?
353                                 //
354                                 return null;
355                         case ERROR:
356                         case REJECT:
357                                 //
358                                 // We'll consider these two codes as exceptions
359                                 //
360                                 this.completeOperation(operationAttempt, appcResponse.getStatus().Description, PolicyResult.FAILURE_EXCEPTION);
361                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
362                                         return null;
363                                 }
364                                 return PolicyResult.FAILURE_EXCEPTION;
365                         case SUCCESS:
366                                 //
367                                 //
368                                 //
369                                 this.completeOperation(operationAttempt, appcResponse.getStatus().Description, PolicyResult.SUCCESS);
370                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
371                                         return null;
372                                 }
373                                 return PolicyResult.SUCCESS;
374                         case FAILURE:
375                                 //
376                                 //
377                                 //
378                                 this.completeOperation(operationAttempt, appcResponse.getStatus().Description, PolicyResult.FAILURE);
379                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
380                                         return null;
381                                 }
382                                 return PolicyResult.FAILURE;
383                         }
384                 }
385                 else if (response instanceof LCMResponseWrapper) {
386
387                     LCMResponseWrapper dmaapResponse = (LCMResponseWrapper) response;
388
389                     /*
390                      * Parse out the operation attempt using the subrequestid
391                      */
392                     Integer operationAttempt = AppcLcmActorServiceProvider.parseOperationAttempt(dmaapResponse.getBody().getCommonHeader().getSubRequestId());
393                     if (operationAttempt == null) {
394                         this.completeOperation(operationAttempt, "Policy was unable to parse APP-C SubRequestID (it was null).", PolicyResult.FAILURE_EXCEPTION);
395                     }
396
397                     /*
398                      * Process the APPCLCM response to see what PolicyResult
399                      * should be returned
400                      */
401                     AbstractMap.SimpleEntry<PolicyResult, String> result = AppcLcmActorServiceProvider.processResponse(dmaapResponse);
402
403                     if (result.getKey() != null) {
404                     this.completeOperation(operationAttempt, result.getValue(), result.getKey());
405                     if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
406                     return null;
407                 }
408                     return result.getKey();
409                     }
410                     return null;
411                 } else if (response instanceof SOResponse) {
412                         SOResponse msoResponse = (SOResponse) response;
413
414                         switch (msoResponse.httpResponseCode) {
415                         case 200:
416                         case 202:
417                                 //
418                                 // Consider it as success
419                                 //
420                                 this.completeOperation(this.attempts, msoResponse.httpResponseCode + " Success", PolicyResult.SUCCESS);
421                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
422                                         return null;
423                                 }
424                                 return PolicyResult.SUCCESS;
425                         default:
426                                 //
427                                 // Consider it as failure
428                                 //
429                                 this.completeOperation(this.attempts, msoResponse.httpResponseCode + " Failed", PolicyResult.FAILURE);
430                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
431                                         return null;
432                                 }
433                                 return PolicyResult.FAILURE;
434                         }
435
436                 } else if (response instanceof VFCResponse) {
437                         VFCResponse vfcResponse = (VFCResponse) response;
438
439                         if (vfcResponse.responseDescriptor.getStatus().equalsIgnoreCase("finished")) {
440                                 //
441                                 // Consider it as success
442                                 //
443                                 this.completeOperation(this.attempts, " Success", PolicyResult.SUCCESS);
444                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
445                                         return null;
446                                 }
447                                 return PolicyResult.SUCCESS;
448                         } else {
449                                 //
450                                 // Consider it as failure
451                                 //
452                                 this.completeOperation(this.attempts, " Failed", PolicyResult.FAILURE);
453                                 if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) {
454                                         return null;
455                                 }
456                                 // increment operation attempts for retries
457                                 this.attempts += 1;
458                                 return PolicyResult.FAILURE;
459                         }
460                 }
461                 return null;
462         }
463
464         public Integer  getOperationTimeout() {
465                 //
466                 // Sanity check
467                 //
468                 if (this.policy == null) {
469                         logger.debug("getOperationTimeout returning 0");
470                         return 0;
471                 }
472                 logger.debug("getOperationTimeout returning {}", this.policy.getTimeout());
473                 return this.policy.getTimeout();
474         }
475
476         public String   getOperationTimeoutString(int defaultTimeout) {
477                 Integer to = this.getOperationTimeout();
478                 if (to == null || to == 0) {
479                         return Integer.toString(defaultTimeout) + "s";
480                 }
481                 return to.toString() + "s";
482         }
483
484         public PolicyResult     getOperationResult() {
485                 return this.policyResult;
486         }
487
488         public String   getOperationMessage() {
489                 if (this.currentOperation != null && this.currentOperation.operation != null) {
490                         return this.currentOperation.operation.toMessage();
491                 }
492
493                 if (!this.operationHistory.isEmpty()) {
494                         return this.operationHistory.getLast().operation.toMessage();
495                 }
496                 return null;
497         }
498
499         public String   getOperationMessage(String guardResult) {
500                 if (this.currentOperation != null && this.currentOperation.operation != null) {
501                         return this.currentOperation.operation.toMessage()+ ", Guard result: " + guardResult;
502                 }
503           
504                 if (!this.operationHistory.isEmpty()) {
505                         return this.operationHistory.getLast().operation.toMessage() + ", Guard result: " + guardResult;
506                 }
507                 return null;
508         }
509
510         public String   getOperationHistory() {
511                 if (this.currentOperation != null && this.currentOperation.operation != null) {
512                         return this.currentOperation.operation.toHistory();
513                 }
514           
515                 if (!this.operationHistory.isEmpty()) {
516                         return this.operationHistory.getLast().operation.toHistory();
517                 }
518                 return null;
519         }
520
521         public LinkedList<ControlLoopOperation> getHistory() {
522                 LinkedList<ControlLoopOperation> history = new LinkedList<ControlLoopOperation>();
523                 for (Operation op : this.operationHistory) {
524                         history.add(new ControlLoopOperation(op.operation));
525
526                 }
527                 return history;
528         }
529
530         public void             setOperationHasTimedOut() {
531                 //
532                 //
533                 //
534                 this.completeOperation(this.attempts, "Operation timed out", PolicyResult.FAILURE_TIMEOUT);
535         }
536
537         public void             setOperationHasGuardDeny() {
538                 //
539                 //
540                 //
541                 this.completeOperation(this.attempts, "Operation denied by Guard", PolicyResult.FAILURE_GUARD);
542         }
543         
544         public void setOperationHasException(String message) {
545                 this.completeOperation(this.attempts, message, PolicyResult.FAILURE_EXCEPTION);
546         }
547
548         public boolean  isOperationComplete() {
549                 //
550                 // Is there currently a result?
551                 //
552                 if (this.policyResult == null) {
553                         //
554                         // either we are in process or we
555                         // haven't started
556                         //
557                         return false;
558                 }
559                 //
560                 // We have some result, check if the operation failed
561                 //
562                 if (this.policyResult.equals(PolicyResult.FAILURE)) {
563                         //
564                         // Check if there were no retries specified
565                         //
566                         if (policy.getRetry() == null || policy.getRetry() == 0) {
567                                 //
568                                 // The result is the failure
569                                 //
570                                 return true;
571                         }
572                         //
573                         // Check retries
574                         //
575                         if (this.isRetriesMaxedOut()) {
576                                 //
577                                 // No more attempts allowed, reset
578                                 // that our actual result is failure due to retries
579                                 //
580                                 this.policyResult = PolicyResult.FAILURE_RETRIES;
581                                 return true;
582                         } else {
583                                 //
584                                 // There are more attempts available to try the
585                                 // policy recipe.
586                                 //
587                                 return false;
588                         }
589                 }
590                 //
591                 // Other results mean we are done
592                 //
593                 return true;
594         }
595
596         public boolean  isOperationRunning() {
597                 return (this.currentOperation != null);
598         }
599
600         private boolean isRetriesMaxedOut() {
601                 if (policy.getRetry() == null || policy.getRetry() == 0) {
602                         //
603                         // There were NO retries specified, so declare
604                         // this as completed.
605                         //
606                         return (this.attempts > 0);
607                 }
608                 return (this.attempts > policy.getRetry());
609         }
610
611         private void    storeOperationInDataBase(){
612                 // Only store in DB if enabled
613                 boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled"));
614                 if( !guardEnabled ){
615                         return;
616                 }
617
618
619                 // DB Properties
620                 Properties props = new Properties();
621                 if(PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL) != null &&
622                                 PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER) != null &&
623                                 PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS) != null){
624                         props.put(Util.ECLIPSE_LINK_KEY_URL, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL));
625                         props.put(Util.ECLIPSE_LINK_KEY_USER, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER));
626                         props.put(Util.ECLIPSE_LINK_KEY_PASS, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS));
627                 }
628                 
629                 
630                 String OpsHistPU = System.getProperty("OperationsHistoryPU");
631                 if(OpsHistPU == null || !OpsHistPU.equals("TestOperationsHistoryPU")){
632                         OpsHistPU = "OperationsHistoryPU";
633                 }
634                 else{
635                         props.clear();
636                 }
637                 EntityManager em;
638                 try{
639                         em = Persistence.createEntityManagerFactory(OpsHistPU, props).createEntityManager();
640                 }catch(Exception e){
641                         logger.error("storeOperationInDataBase threw: ", e);
642                         return;
643                 }
644
645                 OperationsHistoryDbEntry newEntry = new OperationsHistoryDbEntry();
646
647                 newEntry.closedLoopName = this.onset.closedLoopControlName;
648                 newEntry.requestId = this.onset.requestID.toString();
649                 newEntry.actor = this.currentOperation.operation.actor;
650                 newEntry.operation = this.currentOperation.operation.operation;
651                 newEntry.target = this.targetEntity;
652                 newEntry.starttime = Timestamp.from(this.currentOperation.operation.start);
653                 newEntry.subrequestId = this.currentOperation.operation.subRequestId;
654                 newEntry.endtime = new Timestamp(this.currentOperation.operation.end.toEpochMilli());
655                 newEntry.message = this.currentOperation.operation.message;
656                 newEntry.outcome = this.currentOperation.operation.outcome;
657
658                 em.getTransaction().begin();
659                 em.persist(newEntry);
660                 em.getTransaction().commit();
661
662                 em.close();
663
664         }
665
666
667
668         private void    completeOperation(Integer attempt, String message, PolicyResult result) {
669                 if (attempt == null) {
670                         logger.debug("attempt cannot be null (i.e. subRequestID)");
671                         return;
672                 }
673                 if (this.currentOperation != null) {
674                         if (this.currentOperation.attempt == attempt.intValue()) {
675                                 this.currentOperation.operation.end = Instant.now();
676                                 this.currentOperation.operation.message = message;
677                                 this.currentOperation.operation.outcome = result.toString();
678                                 this.currentOperation.policyResult = result;
679                                 //
680                                 // Save it in history
681                                 //
682                                 this.operationHistory.add(this.currentOperation);
683                                 this.storeOperationInDataBase();
684                                 //
685                                 // Set our last result
686                                 //
687                                 this.policyResult = result;
688                                 //
689                                 // Clear the current operation field
690                                 //
691                                 this.currentOperation = null;
692                                 return;
693                         }
694                         logger.debug("not current");
695                 }
696                 for (Operation op : this.operationHistory) {
697                         if (op.attempt == attempt.intValue()) {
698                                 op.operation.end = Instant.now();
699                                 op.operation.message = message;
700                                 op.operation.outcome = result.toString();
701                                 op.policyResult = result;
702                                 return;
703                         }
704                 }
705                 logger.debug("Could not find associated operation");
706
707         }
708
709 }