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