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