3feab7d7cc94679adf80e0344e4b4aca77920340
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * controlloop event manager
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop.eventmanager;
22
23 import java.io.Serializable;
24 import java.io.UnsupportedEncodingException;
25 import java.net.URLDecoder;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.LinkedList;
29 import java.util.UUID;
30
31 import org.onap.policy.controlloop.ControlLoopEventStatus;
32 import org.onap.policy.controlloop.ControlLoopNotificationType;
33 import org.onap.policy.controlloop.ControlLoopOperation;
34 import org.onap.policy.controlloop.VirtualControlLoopEvent;
35 import org.onap.policy.controlloop.VirtualControlLoopNotification;
36 import org.onap.policy.controlloop.ControlLoopException;
37 import org.onap.policy.controlloop.policy.FinalResult;
38 import org.onap.policy.controlloop.policy.Policy;
39 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
40 import org.onap.policy.guard.GuardResult;
41 import org.onap.policy.guard.LockCallback;
42 import org.onap.policy.guard.PolicyGuard;
43 import org.onap.policy.guard.PolicyGuard.LockResult;
44 import org.onap.policy.guard.TargetLock;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 public class ControlLoopEventManager implements LockCallback, Serializable {
49         
50         /**
51          * 
52          */
53         private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
54         
55         private static final long serialVersionUID = -1216568161322872641L;
56         public final String closedLoopControlName;
57         public final UUID requestID;
58         
59         private String controlLoopResult;
60         private ControlLoopProcessor processor = null;
61         private VirtualControlLoopEvent onset;
62         private Integer numOnsets = 0;
63         private Integer numAbatements = 0;
64         private VirtualControlLoopEvent abatement;
65         private FinalResult controlLoopTimedOut = null;
66
67         private boolean isActivated = false;
68         private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
69         private ControlLoopOperationManager currentOperation = null;
70         private TargetLock targetLock = null;
71         
72         private static Collection<String> requiredAAIKeys = new ArrayList<>();
73         static {
74                 requiredAAIKeys.add("AICVServerSelfLink");
75                 requiredAAIKeys.add("AICIdentity");
76                 requiredAAIKeys.add("is_closed_loop_disabled");
77                 requiredAAIKeys.add("VM_NAME");
78         }
79
80         public ControlLoopEventManager(String closedLoopControlName, UUID requestID) {
81                 this.closedLoopControlName = closedLoopControlName;
82                 this.requestID = requestID;
83         }
84         
85         public String getControlLoopResult() {
86                 return controlLoopResult;
87         }
88         
89         public void setControlLoopResult(String controlLoopResult) {
90                 this.controlLoopResult = controlLoopResult;
91         }
92         
93         public Integer getNumOnsets() {
94                 return numOnsets;
95         }
96
97         public void setNumOnsets(Integer numOnsets) {
98                 this.numOnsets = numOnsets;
99         }
100
101         public Integer getNumAbatements() {
102                 return numAbatements;
103         }
104
105         public void setNumAbatements(Integer numAbatements) {
106                 this.numAbatements = numAbatements;
107         }
108
109         public boolean isActivated() {
110                 return isActivated;
111         }
112
113         public void setActivated(boolean isActivated) {
114                 this.isActivated = isActivated;
115         }
116         
117         public VirtualControlLoopEvent  getOnsetEvent() {
118                 return this.onset;
119         }
120         
121         public VirtualControlLoopEvent getAbatementEvent() {
122                 return this.abatement;
123         }
124         
125         public ControlLoopProcessor getProcessor() {
126                 return this.processor;
127         }
128
129         public VirtualControlLoopNotification   activate(VirtualControlLoopEvent event) {
130                 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
131                 try {
132                         //
133                         // This method should ONLY be called ONCE
134                         //
135                         if (this.isActivated) {
136                                 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
137                         }
138                         //
139                         // Syntax check the event
140                         //
141                         checkEventSyntax(event);
142                         //
143                         // At this point we are good to go with this event
144                         //
145                         this.onset = event;
146                         this.numOnsets = 1;
147                         //
148                         notification.notification = ControlLoopNotificationType.ACTIVE;
149                         //
150                         // Set ourselves as active
151                         //
152                         this.isActivated = true;
153                 } catch (ControlLoopException e) {
154                         logger.error("{}: activate threw: ",this, e);
155                         notification.notification = ControlLoopNotificationType.REJECTED;
156                         notification.message = e.getMessage();
157                 }
158                 return notification;
159         }
160         
161         
162         
163         public VirtualControlLoopNotification   activate(String yamlSpecification, VirtualControlLoopEvent event) {
164                 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
165                 try {
166                         //
167                         // This method should ONLY be called ONCE
168                         //
169                         if (this.isActivated) {
170                                 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
171                         }
172                         //
173                         // Syntax check the event
174                         //
175                         checkEventSyntax(event);
176         
177                         //
178                         // Check the YAML
179                         //
180                         if (yamlSpecification == null || yamlSpecification.length() < 1) {
181                                 throw new ControlLoopException("yaml specification is null or 0 length");
182                         }
183                         String decodedYaml = null;
184                         try {
185                                 decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
186                                 if (decodedYaml != null && decodedYaml.length() > 0) {
187                                         yamlSpecification = decodedYaml;
188                                 }
189                         } catch (UnsupportedEncodingException e) {
190                                 logger.error("{}: activate threw: ",this, e);
191                         }
192                         //
193                         // Parse the YAML specification
194                         //
195                         this.processor = new ControlLoopProcessor(yamlSpecification);
196                         
197                         //
198                         // At this point we are good to go with this event
199                         //
200                         this.onset = event;
201                         this.numOnsets = 1;
202                         //
203                         //
204                         //
205                         notification.notification = ControlLoopNotificationType.ACTIVE;
206                         //
207                         // Set ourselves as active
208                         //
209                         this.isActivated = true;
210                 } catch (ControlLoopException e) {
211                         logger.error("{}: activate threw: ",this, e);
212                         notification.notification = ControlLoopNotificationType.REJECTED;
213                         notification.message = e.getMessage();
214                 }
215                 return notification;
216         }
217         
218         public VirtualControlLoopNotification   isControlLoopFinal() throws ControlLoopException {
219                 //
220                 // Check if they activated us
221                 //
222                 if (this.isActivated == false) {
223                         throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
224                 }
225                 //
226                 // Make sure we are expecting this call.
227                 //
228                 if (this.onset == null) {
229                         throw new ControlLoopException("No onset event for ControlLoopEventManager.");
230                 }
231                 //
232                 // Ok, start creating the notification
233                 //
234                 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
235                 //
236                 // Check if the overall control loop has timed out
237                 //
238                 if (this.isControlLoopTimedOut()) {
239                         //
240                         // Yes we have timed out
241                         //
242                         notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
243                         notification.message = "Control Loop timed out";
244                         notification.history.addAll(this.controlLoopHistory);
245                         return notification;                    
246                 }
247                 //
248                 // Check if the current policy is Final
249                 //
250                 FinalResult result = this.processor.checkIsCurrentPolicyFinal();
251                 if (result == null) {
252                         //
253                         // we are not at a final result
254                         //
255                         return null;
256                 }
257         
258                 switch (result) {
259                 case FINAL_FAILURE:
260                 case FINAL_FAILURE_EXCEPTION:
261                 case FINAL_FAILURE_RETRIES:
262                 case FINAL_FAILURE_TIMEOUT:
263                 case FINAL_FAILURE_GUARD:
264                         notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
265                         break;
266                 case FINAL_OPENLOOP:
267                         notification.notification = ControlLoopNotificationType.FINAL_OPENLOOP;
268                         break;
269                 case FINAL_SUCCESS:
270                         notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
271                         break;
272                 default:
273                         return null;
274                 }
275                 //
276                 // Be sure to add all the history
277                 //
278                 notification.history.addAll(this.controlLoopHistory);
279                 return notification;
280         }
281                 
282         public ControlLoopOperationManager      processControlLoop() throws ControlLoopException {
283                 //
284                 // Check if they activated us
285                 //
286                 if (this.isActivated == false) {
287                         throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
288                 }
289                 //
290                 // Make sure we are expecting this call.
291                 //
292                 if (this.onset == null) {
293                         throw new ControlLoopException("No onset event for ControlLoopEventManager.");
294                 }
295                 //
296                 // Is there a current operation?
297                 //
298                 if (this.currentOperation != null) {
299                         //
300                         // Throw an exception, or simply return the current operation?
301                         //
302                         throw new ControlLoopException("Already working an Operation, do not call this method.");
303                 }
304                 //
305                 // Ensure we are not FINAL
306                 //
307                 VirtualControlLoopNotification notification = this.isControlLoopFinal();
308                 if (notification != null) {
309                         //
310                         // This is weird, we require them to call the isControlLoopFinal() method first
311                         //
312                         // We should really abstract this and avoid throwing an exception, because it really
313                         // isn't an exception.
314                         //
315                         throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
316                 }
317                 //
318                 // Not final so get the policy that needs to be worked on.
319                 //
320                 Policy policy = this.processor.getCurrentPolicy();
321                 if (policy == null) {
322                         throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
323                 }
324                 //
325                 // And setup an operation
326                 //
327                 this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
328                 //
329                 // Return it
330                 //
331                 return this.currentOperation;
332         }
333         
334         public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
335                 //
336                 // Verify we have a current operation
337                 //
338                 if (this.currentOperation != null) {
339                         //
340                         // Validate they are finishing the current operation
341                         // PLD - this is simply comparing the policy. Do we want to equals the whole object?
342                         //
343                         if (this.currentOperation.policy.equals(operation.policy)) {
344                                 System.out.println("Finishing " + this.currentOperation.policy.getRecipe() + " result is " + this.currentOperation.getOperationResult());
345                                 //
346                                 // Save history
347                                 //
348                                 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
349                                 //
350                                 // Move to the next Policy
351                                 //
352                                 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
353                                 //
354                                 // Just null this out
355                                 //
356                                 this.currentOperation = null;
357                                 //
358                                 // TODO: Release our lock
359                                 //
360                                 return;
361                         }
362                         System.out.println("Cannot finish current operation " + this.currentOperation.policy + " does not match given operation " + operation.policy);
363                         return;
364                 }
365                 throw new ControlLoopException("No operation to finish.");
366         }
367         
368         public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
369                 //
370                 // Sanity check
371                 //
372                 if (this.currentOperation == null) {
373                         throw new ControlLoopException("Do not have a current operation.");
374                 }
375                 //
376                 // Have we acquired it already?
377                 //
378                 if (this.targetLock != null) {
379                         //
380                         // TODO: Make sure the current lock is for the same target.
381                         // Currently, it should be. But in the future it may not.
382                         //
383                         return new LockResult<GuardResult, TargetLock>(GuardResult.LOCK_ACQUIRED, this.targetLock);
384                 } else {
385                         //
386                         // Ask the Guard
387                         //
388                         LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
389                                                                                                                                                 this.currentOperation.policy.getTarget().getType(), 
390                                                                                                                                                 this.getTargetInstance(this.currentOperation.policy),
391                                                                                                                                                 this.onset.requestID,
392                                                                                                                                                 this);
393                         //
394                         // Was it acquired?
395                         //
396                         if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
397                                 //
398                                 // Yes, let's save it
399                                 //
400                                 this.targetLock = lockResult.getB();
401                         }
402                         return lockResult;
403                 }
404         }
405         
406         public synchronized TargetLock unlockCurrentOperation() {
407                 if (this.targetLock == null) {
408                         return null;
409                 }
410                 if (PolicyGuard.unlockTarget(this.targetLock) == true) {
411                         TargetLock returnLock = this.targetLock;
412                         this.targetLock = null;
413                         return returnLock;
414                 }
415                 return null;
416         }
417         
418         public enum NEW_EVENT_STATUS {
419                 FIRST_ONSET,
420                 SUBSEQUENT_ONSET,
421                 FIRST_ABATEMENT,
422                 SUBSEQUENT_ABATEMENT,
423                 SYNTAX_ERROR
424                 ;
425         }
426                 
427         public NEW_EVENT_STATUS onNewEvent(VirtualControlLoopEvent event) {
428                 try {
429                         ControlLoopEventManager.checkEventSyntax(event);
430                         if (event.closedLoopEventStatus == ControlLoopEventStatus.ONSET) {
431                                 //
432                                 // Check if this is our original ONSET
433                                 //
434                                 if (event.equals(this.onset)) {
435                                         //
436                                         // DO NOT retract it
437                                         //
438                                         return NEW_EVENT_STATUS.FIRST_ONSET;
439                                 }
440                                 //
441                                 // Log that we got an onset
442                                 //
443                                 this.numOnsets++;
444                                 return NEW_EVENT_STATUS.SUBSEQUENT_ONSET;
445                         } else if (event.closedLoopEventStatus == ControlLoopEventStatus.ABATED) {
446                                 //
447                                 // Have we already got an abatement?
448                                 //
449                                 if (this.abatement == null) {
450                                         //
451                                         // Save this
452                                         //
453                                         this.abatement = event;
454                                         //
455                                         // Keep track that we received another
456                                         //
457                                         this.numAbatements++;
458                                         //
459                                         //
460                                         //
461                                         return NEW_EVENT_STATUS.FIRST_ABATEMENT;
462                                 } else {
463                                         //
464                                         // Keep track that we received another
465                                         //
466                                         this.numAbatements++;
467                                         //
468                                         //
469                                         //
470                                         return NEW_EVENT_STATUS.SUBSEQUENT_ABATEMENT;
471                                 }
472                         } else {
473                                 return NEW_EVENT_STATUS.SYNTAX_ERROR;
474                         }
475                 } catch (ControlLoopException e) {
476                         logger.error("{}: onNewEvent threw: ",this, e);
477                         return NEW_EVENT_STATUS.SYNTAX_ERROR;
478                 }
479         }
480         
481         public VirtualControlLoopNotification setControlLoopTimedOut() {
482                 this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
483                 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
484                 notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
485                 notification.message = "Control Loop timed out";
486                 notification.history.addAll(this.controlLoopHistory);
487                 return notification;                    
488         }
489         
490         public boolean isControlLoopTimedOut() {
491                 return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
492         }
493         
494         public int      getControlLoopTimeout(Integer defaultTimeout) {
495                 if (this.processor != null && this.processor.getControlLoop() != null) {
496                         return this.processor.getControlLoop().getTimeout();
497                 }
498                 if (defaultTimeout != null) {
499                         return defaultTimeout;
500                 }
501                 return 0;
502         }
503         
504         public static void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
505                 if (event.closedLoopEventStatus == null || 
506                                 (event.closedLoopEventStatus != ControlLoopEventStatus.ONSET &&
507                                 event.closedLoopEventStatus != ControlLoopEventStatus.ABATED)) {
508                         throw new ControlLoopException("Invalid value in closedLoopEventStatus");
509                 }
510                 if (event.closedLoopControlName == null || event.closedLoopControlName.length() < 1) {
511                         throw new ControlLoopException("No control loop name");
512                 }
513                 if (event.requestID == null) {
514                         throw new ControlLoopException("No request ID");
515                 }
516                 if (event.AAI == null) {
517                         throw new ControlLoopException("AAI is null");
518                 }
519                 if (event.AAI.get("vserver.is-closed-loop-disabled") == null) {
520                         throw new ControlLoopException("vserver.is-closed-loop-disabled information missing");
521                 }
522                 if (event.AAI.get("vserver.is-closed-loop-disabled").equalsIgnoreCase("true") ||
523                                 event.AAI.get("vserver.is-closed-loop-disabled").equalsIgnoreCase("T") ||
524                                 event.AAI.get("vserver.is-closed-loop-disabled").equalsIgnoreCase("yes") ||
525                                 event.AAI.get("vserver.is-closed-loop-disabled").equalsIgnoreCase("Y")) {
526                         throw new ControlLoopException("vserver.is-closed-loop-disabled is set to true");
527                 }
528                 if (event.target == null || event.target.length() < 1) {
529                         throw new ControlLoopException("No target field");
530                 } else {
531                         if (! event.target.equalsIgnoreCase("VM_NAME") &&
532                                 ! event.target.equalsIgnoreCase("VNF_NAME") &&
533                                 ! event.target.equalsIgnoreCase("vserver.vserver-name") &&
534                                 ! event.target.equalsIgnoreCase("generic-vnf.vnf-name") ) {
535                                 throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
536                         }
537                 }
538         }
539
540         @Override
541         public boolean isActive() {
542                 // TODO
543                 return true;
544         }
545
546         @Override
547         public boolean releaseLock() {
548                 // TODO
549                 return false;
550         }
551
552         public String getTargetInstance(Policy policy) {
553                 if (policy.getTarget() != null) {
554                         if (policy.getTarget().getType() != null) {
555                                 switch(policy.getTarget().getType()) {
556                                 case PNF:
557                                         break;
558                                 case VM:
559                                         if (this.onset.target.equalsIgnoreCase("vserver.vserver-name")) {
560                                                 return this.onset.AAI.get("vserver.vserver-name");
561                                         }
562                                         break;
563                                 default:
564                                         break;
565                                 }
566                         }
567                 }
568                 return null;
569         }
570
571         @Override
572         public String toString() {
573                 return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestID=" + requestID
574                                 + ", processor=" + processor + ", onset=" + (onset != null ? onset.requestID : "null") + ", numOnsets=" + numOnsets + ", numAbatements="
575                                 + numAbatements + ", isActivated="
576                                 + isActivated + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
577         }
578         
579 }