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