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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.controlloop.eventmanager;
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;
31 import org.onap.policy.aai.AAIGETVnfResponse;
32 import org.onap.policy.aai.AAIGETVserverResponse;
33 import org.onap.policy.aai.AAIManager;
34 import org.onap.policy.aai.util.AAIException;
35 import org.onap.policy.controlloop.ControlLoopEventStatus;
36 import org.onap.policy.controlloop.ControlLoopException;
37 import org.onap.policy.controlloop.ControlLoopNotificationType;
38 import org.onap.policy.controlloop.ControlLoopOperation;
39 import org.onap.policy.controlloop.VirtualControlLoopEvent;
40 import org.onap.policy.controlloop.VirtualControlLoopNotification;
41 import org.onap.policy.controlloop.policy.FinalResult;
42 import org.onap.policy.controlloop.policy.Policy;
43 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
44 import org.onap.policy.guard.GuardResult;
45 import org.onap.policy.guard.LockCallback;
46 import org.onap.policy.guard.PolicyGuard;
47 import org.onap.policy.guard.PolicyGuard.LockResult;
48 import org.onap.policy.guard.TargetLock;
49 import org.onap.policy.rest.RESTManager;
50 import org.onap.policy.drools.system.PolicyEngine;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 public class ControlLoopEventManager implements LockCallback, Serializable {
59 private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
61 private static final long serialVersionUID = -1216568161322872641L;
62 public final String closedLoopControlName;
63 public final UUID requestID;
65 private String controlLoopResult;
66 private transient ControlLoopProcessor processor = null;
67 private VirtualControlLoopEvent onset;
68 private Integer numOnsets = 0;
69 private Integer numAbatements = 0;
70 private VirtualControlLoopEvent abatement;
71 private FinalResult controlLoopTimedOut = null;
73 private boolean isActivated = false;
74 private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
75 private ControlLoopOperationManager currentOperation = null;
76 private transient TargetLock targetLock = null;
77 private AAIGETVnfResponse vnfResponse = null;
78 private AAIGETVserverResponse vserverResponse = null;
79 private static String aaiHostURL;
80 private static String aaiUser;
81 private static String aaiPassword;
83 private static Collection<String> requiredAAIKeys = new ArrayList<>();
85 requiredAAIKeys.add("AICVServerSelfLink");
86 requiredAAIKeys.add("AICIdentity");
87 requiredAAIKeys.add("is_closed_loop_disabled");
88 requiredAAIKeys.add("VM_NAME");
91 public ControlLoopEventManager(String closedLoopControlName, UUID requestID) {
92 this.closedLoopControlName = closedLoopControlName;
93 this.requestID = requestID;
96 public String getControlLoopResult() {
97 return controlLoopResult;
100 public void setControlLoopResult(String controlLoopResult) {
101 this.controlLoopResult = controlLoopResult;
104 public Integer getNumOnsets() {
108 public void setNumOnsets(Integer numOnsets) {
109 this.numOnsets = numOnsets;
112 public Integer getNumAbatements() {
113 return numAbatements;
116 public void setNumAbatements(Integer numAbatements) {
117 this.numAbatements = numAbatements;
120 public boolean isActivated() {
124 public void setActivated(boolean isActivated) {
125 this.isActivated = isActivated;
128 public VirtualControlLoopEvent getOnsetEvent() {
132 public VirtualControlLoopEvent getAbatementEvent() {
133 return this.abatement;
136 public ControlLoopProcessor getProcessor() {
137 return this.processor;
140 public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
141 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
144 // This method should ONLY be called ONCE
146 if (this.isActivated) {
147 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
150 // Syntax check the event
152 checkEventSyntax(event);
155 // At this point we are good to go with this event
160 notification.notification = ControlLoopNotificationType.ACTIVE;
162 // Set ourselves as active
164 this.isActivated = true;
165 } catch (ControlLoopException e) {
166 logger.error("{}: activate threw: ",this, e);
167 notification.notification = ControlLoopNotificationType.REJECTED;
168 notification.message = e.getMessage();
175 public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
176 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
179 // This method should ONLY be called ONCE
181 if (this.isActivated) {
182 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
185 // Syntax check the event
187 checkEventSyntax(event);
192 if (yamlSpecification == null || yamlSpecification.length() < 1) {
193 throw new ControlLoopException("yaml specification is null or 0 length");
195 String decodedYaml = null;
197 decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
198 if (decodedYaml != null && decodedYaml.length() > 0) {
199 yamlSpecification = decodedYaml;
201 } catch (UnsupportedEncodingException e) {
202 logger.error("{}: activate threw: ",this, e);
205 // Parse the YAML specification
207 this.processor = new ControlLoopProcessor(yamlSpecification);
210 // At this point we are good to go with this event
217 notification.notification = ControlLoopNotificationType.ACTIVE;
219 // Set ourselves as active
221 this.isActivated = true;
222 } catch (ControlLoopException e) {
223 logger.error("{}: activate threw: ",this, e);
224 notification.notification = ControlLoopNotificationType.REJECTED;
225 notification.message = e.getMessage();
230 public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
232 // Check if they activated us
234 if (this.isActivated == false) {
235 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
238 // Make sure we are expecting this call.
240 if (this.onset == null) {
241 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
244 // Ok, start creating the notification
246 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
248 // Check if the overall control loop has timed out
250 if (this.isControlLoopTimedOut()) {
252 // Yes we have timed out
254 notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
255 notification.message = "Control Loop timed out";
256 notification.history.addAll(this.controlLoopHistory);
260 // Check if the current policy is Final
262 FinalResult result = this.processor.checkIsCurrentPolicyFinal();
263 if (result == null) {
265 // we are not at a final result
271 case FINAL_FAILURE_EXCEPTION:
272 notification.message = "Exception in processing closed loop";
274 case FINAL_FAILURE_RETRIES:
275 case FINAL_FAILURE_TIMEOUT:
276 case FINAL_FAILURE_GUARD:
277 notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
280 notification.notification = ControlLoopNotificationType.FINAL_OPENLOOP;
283 notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
289 // Be sure to add all the history
291 notification.history.addAll(this.controlLoopHistory);
295 public ControlLoopOperationManager processControlLoop() throws ControlLoopException, AAIException {
297 // Check if they activated us
299 if (this.isActivated == false) {
300 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
303 // Make sure we are expecting this call.
305 if (this.onset == null) {
306 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
309 // Is there a current operation?
311 if (this.currentOperation != null) {
313 // Throw an exception, or simply return the current operation?
315 throw new ControlLoopException("Already working an Operation, do not call this method.");
318 // Ensure we are not FINAL
320 VirtualControlLoopNotification notification = this.isControlLoopFinal();
321 if (notification != null) {
323 // This is weird, we require them to call the isControlLoopFinal() method first
325 // We should really abstract this and avoid throwing an exception, because it really
326 // isn't an exception.
328 throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
331 // Not final so get the policy that needs to be worked on.
333 Policy policy = this.processor.getCurrentPolicy();
334 if (policy == null) {
335 throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
338 // And setup an operation
340 this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
344 return this.currentOperation;
347 public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
349 // Verify we have a current operation
351 if (this.currentOperation != null) {
353 // Validate they are finishing the current operation
354 // PLD - this is simply comparing the policy. Do we want to equals the whole object?
356 if (this.currentOperation.policy.equals(operation.policy)) {
357 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(), this.currentOperation.getOperationResult());
361 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
363 // Move to the next Policy
365 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
367 // Just null this out
369 this.currentOperation = null;
371 // TODO: Release our lock
375 logger.debug("Cannot finish current operation {} does not match given operation {}", this.currentOperation.policy, operation.policy);
378 throw new ControlLoopException("No operation to finish.");
381 public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
385 if (this.currentOperation == null) {
386 throw new ControlLoopException("Do not have a current operation.");
389 // Have we acquired it already?
391 if (this.targetLock != null) {
393 // TODO: Make sure the current lock is for the same target.
394 // Currently, it should be. But in the future it may not.
396 return new LockResult<GuardResult, TargetLock>(GuardResult.LOCK_ACQUIRED, this.targetLock);
401 LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
402 this.currentOperation.policy.getTarget().getType(),
403 this.currentOperation.getTargetEntity(),
404 this.onset.requestID,
409 if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
411 // Yes, let's save it
413 this.targetLock = lockResult.getB();
419 public synchronized TargetLock unlockCurrentOperation() {
420 if (this.targetLock == null) {
423 if (PolicyGuard.unlockTarget(this.targetLock) == true) {
424 TargetLock returnLock = this.targetLock;
425 this.targetLock = null;
431 public enum NEW_EVENT_STATUS {
435 SUBSEQUENT_ABATEMENT,
440 public NEW_EVENT_STATUS onNewEvent(VirtualControlLoopEvent event) throws AAIException {
442 this.checkEventSyntax(event);
443 if (event.closedLoopEventStatus == ControlLoopEventStatus.ONSET) {
445 // Check if this is our original ONSET
447 if (event.equals(this.onset)) {
449 // Query A&AI if needed
456 return NEW_EVENT_STATUS.FIRST_ONSET;
459 // Log that we got an onset
462 return NEW_EVENT_STATUS.SUBSEQUENT_ONSET;
463 } else if (event.closedLoopEventStatus == ControlLoopEventStatus.ABATED) {
465 // Have we already got an abatement?
467 if (this.abatement == null) {
471 this.abatement = event;
473 // Keep track that we received another
475 this.numAbatements++;
479 return NEW_EVENT_STATUS.FIRST_ABATEMENT;
482 // Keep track that we received another
484 this.numAbatements++;
488 return NEW_EVENT_STATUS.SUBSEQUENT_ABATEMENT;
491 return NEW_EVENT_STATUS.SYNTAX_ERROR;
493 } catch (ControlLoopException e) {
494 logger.error("{}: onNewEvent threw: ",this, e);
495 return NEW_EVENT_STATUS.SYNTAX_ERROR;
499 public VirtualControlLoopNotification setControlLoopTimedOut() {
500 this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
501 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
502 notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
503 notification.message = "Control Loop timed out";
504 notification.history.addAll(this.controlLoopHistory);
508 public boolean isControlLoopTimedOut() {
509 return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
512 public int getControlLoopTimeout(Integer defaultTimeout) {
513 if (this.processor != null && this.processor.getControlLoop() != null) {
514 return this.processor.getControlLoop().getTimeout();
516 if (defaultTimeout != null) {
517 return defaultTimeout;
522 public AAIGETVnfResponse getVnfResponse() {
526 public AAIGETVserverResponse getVserverResponse() {
527 return vserverResponse;
530 public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
531 if (event.closedLoopEventStatus == null ||
532 (event.closedLoopEventStatus != ControlLoopEventStatus.ONSET &&
533 event.closedLoopEventStatus != ControlLoopEventStatus.ABATED)) {
534 throw new ControlLoopException("Invalid value in closedLoopEventStatus");
536 if (event.closedLoopControlName == null || event.closedLoopControlName.length() < 1) {
537 throw new ControlLoopException("No control loop name");
539 if (event.requestID == null) {
540 throw new ControlLoopException("No request ID");
542 if (event.closedLoopEventStatus == ControlLoopEventStatus.ABATED) {
545 if (event.target == null || event.target.length() < 1) {
546 throw new ControlLoopException("No target field");
547 } else if (! "VM_NAME".equalsIgnoreCase(event.target) &&
548 ! "VNF_NAME".equalsIgnoreCase(event.target) &&
549 ! "vserver.vserver-name".equalsIgnoreCase(event.target) &&
550 ! "generic-vnf.vnf-id".equalsIgnoreCase(event.target) &&
551 ! "generic-vnf.vnf-name".equalsIgnoreCase(event.target) ) {
552 throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
554 if (event.AAI == null) {
555 throw new ControlLoopException("AAI is null");
557 if (event.AAI.get("generic-vnf.vnf-id") == null && event.AAI.get("vserver.vserver-name") == null &&
558 event.AAI.get("generic-vnf.vnf-name") == null) {
559 throw new ControlLoopException("generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
563 public void queryAai(VirtualControlLoopEvent event) throws AAIException {
564 if (event.AAI.get("vserver.is-closed-loop-disabled") == null &&
565 event.AAI.get("generic-vnf.is-closed-loop-disabled") == null) {
567 if (event.AAI.get("generic-vnf.vnf-id") != null) {
568 vnfResponse = getAAIVnfInfo(event);
569 if (vnfResponse == null) {
570 throw new AAIException("AAI Response is null (query by vnf-id)");
572 if (vnfResponse.getRequestError() != null) {
573 throw new AAIException("AAI Responded with a request error (query by vnf-id)");
575 if (isClosedLoopDisabled(vnfResponse) == true) {
576 throw new AAIException("is-closed-loop-disabled is set to true");
578 } else if (event.AAI.get("generic-vnf.vnf-name") != null) {
579 vnfResponse = getAAIVnfInfo(event);
580 if (vnfResponse == null) {
581 throw new AAIException("AAI Response is null (query by vnf-name)");
583 if (vnfResponse.getRequestError() != null) {
584 throw new AAIException("AAI Responded with a request error (query by vnf-name)");
586 if (isClosedLoopDisabled(vnfResponse) == true) {
587 throw new AAIException("is-closed-loop-disabled is set to true");
589 } else if (event.AAI.get("vserver.vserver-name") != null) {
590 vserverResponse = getAAIVserverInfo(event);
591 if (vserverResponse == null) {
592 throw new AAIException("AAI Response is null (query by vserver-name)");
594 if (vserverResponse.getRequestError() != null) {
595 throw new AAIException("AAI responded with a request error (query by vserver-name)");
597 if (isClosedLoopDisabled(vserverResponse) == true) {
598 throw new AAIException("is-closed-loop-disabled is set to true");
601 } catch (Exception e) {
602 logger.error("Exception from getAAIInfo: ", e);
603 throw new AAIException("Exception from getAAIInfo: " + e.toString());
605 } else if (isClosedLoopDisabled(event)) {
606 throw new AAIException("is-closed-loop-disabled is set to true");
610 public static boolean isClosedLoopDisabled(AAIGETVnfResponse aaiResponse) {
611 if (aaiResponse != null && aaiResponse.getIsClosedLoopDisabled() != null) {
612 String value = aaiResponse.getIsClosedLoopDisabled();
613 if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
614 "yes".equalsIgnoreCase(value) || "Y".equalsIgnoreCase(value)) {
622 public static boolean isClosedLoopDisabled(AAIGETVserverResponse aaiResponse) {
623 if (aaiResponse != null && aaiResponse.getIsClosedLoopDisabled() != null) {
624 String value = aaiResponse.getIsClosedLoopDisabled();
625 if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
626 "yes".equalsIgnoreCase(value) || "Y".equalsIgnoreCase(value)) {
634 public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
635 if ("true".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) ||
636 "T".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) ||
637 "yes".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) ||
638 "Y".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled"))) {
641 else if ("true".equalsIgnoreCase(event.AAI.get("generic-vnf.is-closed-loop-disabled")) ||
642 "T".equalsIgnoreCase(event.AAI.get("generic-vnf.is-closed-loop-disabled")) ||
643 "yes".equalsIgnoreCase(event.AAI.get("generic-vnf.is-closed-loop-disabled")) ||
644 "Y".equalsIgnoreCase(event.AAI.get("generic-vnf.is-closed-loop-disabled"))) {
650 public static AAIGETVserverResponse getAAIVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
651 UUID requestID = event.requestID;
652 AAIGETVserverResponse response = null;
653 String vserverName = event.AAI.get("vserver.vserver-name");
656 if (vserverName != null) {
657 aaiHostURL = PolicyEngine.manager.getEnvironmentProperty("aai.url");
658 aaiUser = PolicyEngine.manager.getEnvironmentProperty("aai.username");
659 aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
660 String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
661 String url = aaiHostURL + aaiGetQueryByVserver;
662 logger.info("url: " + url);
663 response = new AAIManager(new RESTManager()).getQueryByVserverName(url, aaiUser, aaiPassword, requestID, vserverName);
665 } catch (Exception e) {
666 logger.error("getAAIVserverInfo exception: ", e);
667 throw new ControlLoopException("Exception in getAAIVserverInfo: ", e);
673 public static AAIGETVnfResponse getAAIVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
674 UUID requestID = event.requestID;
675 AAIGETVnfResponse response = null;
676 String vnfName = event.AAI.get("generic-vnf.vnf-name");
677 String vnfID = event.AAI.get("generic-vnf.vnf-id");
679 aaiHostURL = PolicyEngine.manager.getEnvironmentProperty("aai.url");
680 aaiUser = PolicyEngine.manager.getEnvironmentProperty("aai.username");
681 aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
684 if (vnfName != null) {
685 String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
686 String url = aaiHostURL + aaiGetQueryByVnfName;
687 logger.info("url: " + url);
688 response = new AAIManager(new RESTManager()).getQueryByVnfName(url, aaiUser, aaiPassword, requestID, vnfName);
689 } else if (vnfID != null) {
690 String aaiGetQueryByVnfID = "/aai/v11/network/generic-vnfs/generic-vnf/";
691 String url = aaiHostURL + aaiGetQueryByVnfID;
692 logger.info("url: " + url);
693 response = new AAIManager(new RESTManager()).getQueryByVnfID(url, aaiUser, aaiPassword, requestID, vnfID);
695 } catch (Exception e) {
696 logger.error("getAAIVnfInfo exception: ", e);
697 throw new ControlLoopException("Exception in getAAIVnfInfo: ", e);
704 public boolean isActive() {
710 public boolean releaseLock() {
716 public String toString() {
717 return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestID=" + requestID
718 + ", processor=" + processor + ", onset=" + (onset != null ? onset.requestID : "null") + ", numOnsets=" + numOnsets + ", numAbatements="
719 + numAbatements + ", isActivated="
720 + isActivated + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";