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