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 {
55 private static final String VM_NAME = "VM_NAME";
56 private static final String VNF_NAME = "VNF_NAME";
57 private static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id";
58 private static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name";
59 private static final String VSERVER_VSERVER_NAME = "vserver.vserver-name";
60 private static final String GENERIC_VNF_IS_CLOSED_LOOP_DISABLED = "generic-vnf.is-closed-loop-disabled";
61 private static final String VSERVER_IS_CLOSED_LOOP_DISABLED = "vserver.is-closed-loop-disabled";
63 private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
65 private static final long serialVersionUID = -1216568161322872641L;
66 public final String closedLoopControlName;
67 public final UUID requestID;
69 private String controlLoopResult;
70 private transient ControlLoopProcessor processor = null;
71 private VirtualControlLoopEvent onset;
72 private Integer numOnsets = 0;
73 private Integer numAbatements = 0;
74 private VirtualControlLoopEvent abatement;
75 private FinalResult controlLoopTimedOut = null;
77 private boolean isActivated = false;
78 private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
79 private ControlLoopOperationManager currentOperation = null;
80 private transient TargetLock targetLock = null;
81 private AAIGETVnfResponse vnfResponse = null;
82 private AAIGETVserverResponse vserverResponse = null;
83 private static String aaiHostURL;
84 private static String aaiUser;
85 private static String aaiPassword;
87 private static Collection<String> requiredAAIKeys = new ArrayList<>();
89 requiredAAIKeys.add("AICVServerSelfLink");
90 requiredAAIKeys.add("AICIdentity");
91 requiredAAIKeys.add("is_closed_loop_disabled");
92 requiredAAIKeys.add(VM_NAME);
95 public ControlLoopEventManager(String closedLoopControlName, UUID requestID) {
96 this.closedLoopControlName = closedLoopControlName;
97 this.requestID = requestID;
100 public String getControlLoopResult() {
101 return controlLoopResult;
104 public void setControlLoopResult(String controlLoopResult) {
105 this.controlLoopResult = controlLoopResult;
108 public Integer getNumOnsets() {
112 public void setNumOnsets(Integer numOnsets) {
113 this.numOnsets = numOnsets;
116 public Integer getNumAbatements() {
117 return numAbatements;
120 public void setNumAbatements(Integer numAbatements) {
121 this.numAbatements = numAbatements;
124 public boolean isActivated() {
128 public void setActivated(boolean isActivated) {
129 this.isActivated = isActivated;
132 public VirtualControlLoopEvent getOnsetEvent() {
136 public VirtualControlLoopEvent getAbatementEvent() {
137 return this.abatement;
140 public ControlLoopProcessor getProcessor() {
141 return this.processor;
144 public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
145 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
148 // This method should ONLY be called ONCE
150 if (this.isActivated) {
151 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
154 // Syntax check the event
156 checkEventSyntax(event);
159 // At this point we are good to go with this event
164 notification.setNotification(ControlLoopNotificationType.ACTIVE);
166 // Set ourselves as active
168 this.isActivated = true;
169 } catch (ControlLoopException e) {
170 logger.error("{}: activate by event threw: ", this, e);
171 notification.setNotification(ControlLoopNotificationType.REJECTED);
172 notification.setMessage(e.getMessage());
177 public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
178 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
181 // This method should ONLY be called ONCE
183 if (this.isActivated) {
184 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
187 // Syntax check the event
189 checkEventSyntax(event);
194 if (yamlSpecification == null || yamlSpecification.length() < 1) {
195 throw new ControlLoopException("yaml specification is null or 0 length");
197 } catch (ControlLoopException e) {
198 logger.error("{}: activate by YAML specification and event threw: ",this, e);
199 notification.setNotification(ControlLoopNotificationType.REJECTED);
200 notification.setMessage(e.getMessage());
204 String decodedYaml = null;
206 decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
207 if (decodedYaml != null && decodedYaml.length() > 0) {
208 yamlSpecification = decodedYaml;
210 } catch (UnsupportedEncodingException e) {
211 logger.error("{}: YAML decode in activate by YAML specification and event threw: ", this, e);
212 notification.setNotification(ControlLoopNotificationType.REJECTED);
213 notification.setMessage(e.getMessage());
219 // Parse the YAML specification
221 this.processor = new ControlLoopProcessor(yamlSpecification);
224 // At this point we are good to go with this event
231 notification.setNotification(ControlLoopNotificationType.ACTIVE);
233 // Set ourselves as active
235 this.isActivated = true;
236 } catch (ControlLoopException e) {
237 logger.error("{}: activate by YAML specification and event threw: ",this, e);
238 notification.setNotification(ControlLoopNotificationType.REJECTED);
239 notification.setMessage(e.getMessage());
244 public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
246 // Check if they activated us
248 if (!this.isActivated) {
249 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
252 // Make sure we are expecting this call.
254 if (this.onset == null) {
255 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
258 // Ok, start creating the notification
260 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
262 // Check if the overall control loop has timed out
264 if (this.isControlLoopTimedOut()) {
266 // Yes we have timed out
268 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
269 notification.setMessage("Control Loop timed out");
270 notification.getHistory().addAll(this.controlLoopHistory);
274 // Check if the current policy is Final
276 FinalResult result = this.processor.checkIsCurrentPolicyFinal();
277 if (result == null) {
279 // we are not at a final result
285 case FINAL_FAILURE_EXCEPTION:
286 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
287 notification.setMessage("Exception in processing closed loop");
290 case FINAL_FAILURE_RETRIES:
291 case FINAL_FAILURE_TIMEOUT:
292 case FINAL_FAILURE_GUARD:
293 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
296 notification.setNotification(ControlLoopNotificationType.FINAL_OPENLOOP);
299 notification.setNotification(ControlLoopNotificationType.FINAL_SUCCESS);
305 // Be sure to add all the history
307 notification.getHistory().addAll(this.controlLoopHistory);
311 public ControlLoopOperationManager processControlLoop() throws ControlLoopException, AAIException {
313 // Check if they activated us
315 if (!this.isActivated) {
316 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
319 // Make sure we are expecting this call.
321 if (this.onset == null) {
322 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
325 // Is there a current operation?
327 if (this.currentOperation != null) {
329 // Throw an exception, or simply return the current operation?
331 throw new ControlLoopException("Already working an Operation, do not call this method.");
334 // Ensure we are not FINAL
336 VirtualControlLoopNotification notification = this.isControlLoopFinal();
337 if (notification != null) {
339 // This is weird, we require them to call the isControlLoopFinal() method first
341 // We should really abstract this and avoid throwing an exception, because it really
342 // isn't an exception.
344 throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
347 // Not final so get the policy that needs to be worked on.
349 Policy policy = this.processor.getCurrentPolicy();
350 if (policy == null) {
351 throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
354 // And setup an operation
356 this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
360 return this.currentOperation;
363 public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
365 // Verify we have a current operation
367 if (this.currentOperation != null) {
369 // Validate they are finishing the current operation
370 // PLD - this is simply comparing the policy. Do we want to equals the whole object?
372 if (this.currentOperation.policy.equals(operation.policy)) {
373 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(), this.currentOperation.getOperationResult());
377 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
379 // Move to the next Policy
381 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
383 // Just null this out
385 this.currentOperation = null;
387 // TODO: Release our lock
391 logger.debug("Cannot finish current operation {} does not match given operation {}", this.currentOperation.policy, operation.policy);
394 throw new ControlLoopException("No operation to finish.");
397 public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
401 if (this.currentOperation == null) {
402 throw new ControlLoopException("Do not have a current operation.");
405 // Have we acquired it already?
407 if (this.targetLock != null) {
409 // TODO: Make sure the current lock is for the same target.
410 // Currently, it should be. But in the future it may not.
412 return new LockResult<>(GuardResult.LOCK_ACQUIRED, this.targetLock);
417 LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
418 this.currentOperation.policy.getTarget().getType(),
419 this.currentOperation.getTargetEntity(),
420 this.onset.getRequestID(),
425 if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
427 // Yes, let's save it
429 this.targetLock = lockResult.getB();
435 public synchronized TargetLock unlockCurrentOperation() {
436 if (this.targetLock == null) {
439 if (PolicyGuard.unlockTarget(this.targetLock)) {
440 TargetLock returnLock = this.targetLock;
441 this.targetLock = null;
447 public enum NEW_EVENT_STATUS {
451 SUBSEQUENT_ABATEMENT,
456 public NEW_EVENT_STATUS onNewEvent(VirtualControlLoopEvent event) throws AAIException {
458 this.checkEventSyntax(event);
459 if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ONSET) {
461 // Check if this is our original ONSET
463 if (event.equals(this.onset)) {
465 // Query A&AI if needed
472 return NEW_EVENT_STATUS.FIRST_ONSET;
475 // Log that we got an onset
478 return NEW_EVENT_STATUS.SUBSEQUENT_ONSET;
480 else if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
482 // Have we already got an abatement?
484 if (this.abatement == null) {
488 this.abatement = event;
490 // Keep track that we received another
492 this.numAbatements++;
496 return NEW_EVENT_STATUS.FIRST_ABATEMENT;
500 // Keep track that we received another
502 this.numAbatements++;
506 return NEW_EVENT_STATUS.SUBSEQUENT_ABATEMENT;
510 catch (ControlLoopException e) {
511 logger.error("{}: onNewEvent threw: ",this, e);
513 return NEW_EVENT_STATUS.SYNTAX_ERROR;
516 public VirtualControlLoopNotification setControlLoopTimedOut() {
517 this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
518 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
519 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
520 notification.setMessage("Control Loop timed out");
521 notification.getHistory().addAll(this.controlLoopHistory);
525 public boolean isControlLoopTimedOut() {
526 return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
529 public int getControlLoopTimeout(Integer defaultTimeout) {
530 if (this.processor != null && this.processor.getControlLoop() != null) {
531 return this.processor.getControlLoop().getTimeout();
533 if (defaultTimeout != null) {
534 return defaultTimeout;
539 public AAIGETVnfResponse getVnfResponse() {
543 public AAIGETVserverResponse getVserverResponse() {
544 return vserverResponse;
547 public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
548 if (event.getClosedLoopEventStatus() == null ||
549 (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET &&
550 event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED)) {
551 throw new ControlLoopException("Invalid value in closedLoopEventStatus");
553 if (event.getClosedLoopControlName() == null || event.getClosedLoopControlName().length() < 1) {
554 throw new ControlLoopException("No control loop name");
556 if (event.getRequestID() == null) {
557 throw new ControlLoopException("No request ID");
559 if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
562 if (event.getTarget() == null || event.getTarget().length() < 1) {
563 throw new ControlLoopException("No target field");
564 } else if (! VM_NAME.equalsIgnoreCase(event.getTarget()) &&
565 ! VNF_NAME.equalsIgnoreCase(event.getTarget()) &&
566 ! VSERVER_VSERVER_NAME.equalsIgnoreCase(event.getTarget()) &&
567 ! GENERIC_VNF_VNF_ID.equalsIgnoreCase(event.getTarget()) &&
568 ! GENERIC_VNF_VNF_NAME.equalsIgnoreCase(event.getTarget()) ) {
569 throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
571 if (event.getAAI() == null) {
572 throw new ControlLoopException("AAI is null");
574 if (event.getAAI().get(GENERIC_VNF_VNF_ID) == null && event.getAAI().get(VSERVER_VSERVER_NAME) == null &&
575 event.getAAI().get(GENERIC_VNF_VNF_NAME) == null) {
576 throw new ControlLoopException("generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
580 public void queryAai(VirtualControlLoopEvent event) throws AAIException {
581 if ((event.getAAI().get(VSERVER_IS_CLOSED_LOOP_DISABLED) != null || event.getAAI().get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED) != null) && isClosedLoopDisabled(event)) {
582 throw new AAIException("is-closed-loop-disabled is set to true on VServer or VNF");
586 if (event.getAAI().get(GENERIC_VNF_VNF_ID) != null || event.getAAI().get(GENERIC_VNF_VNF_NAME) != null) {
587 vnfResponse = getAAIVnfInfo(event);
588 processVNFResponse(vnfResponse, event.getAAI().get(GENERIC_VNF_VNF_ID) != null);
590 else if (event.getAAI().get(VSERVER_VSERVER_NAME) != null) {
591 vserverResponse = getAAIVserverInfo(event);
592 processVServerResponse(vserverResponse);
594 } catch (Exception e) {
595 logger.error("Exception from queryAai: ", e);
596 throw new AAIException("Exception from queryAai: " + e.toString());
600 private static void processVNFResponse(AAIGETVnfResponse aaiResponse, boolean queryByVNFID) throws AAIException {
601 String queryTypeString = (queryByVNFID ? "vnf-id" : "vnf-name");
603 if (aaiResponse == null) {
604 throw new AAIException("AAI Response is null (query by " + queryTypeString + ")");
606 if (aaiResponse.getRequestError() != null) {
607 throw new AAIException("AAI Responded with a request error (query by " + queryTypeString + ")");
610 if (aaiResponse.getIsClosedLoopDisabled() != null) {
611 String value = aaiResponse.getIsClosedLoopDisabled();
612 if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
613 "yes".equalsIgnoreCase(value) || "Y".equalsIgnoreCase(value)) {
614 throw new AAIException("is-closed-loop-disabled is set to true (query by " + queryTypeString + ")");
619 private static void processVServerResponse(AAIGETVserverResponse aaiResponse) throws AAIException {
620 if (aaiResponse == null) {
621 throw new AAIException("AAI Response is null (query by vserver-name)");
623 if (aaiResponse.getRequestError() != null) {
624 throw new AAIException("AAI responded with a request error (query by vserver-name)");
627 if (aaiResponse.getIsClosedLoopDisabled() != null) {
628 String value = aaiResponse.getIsClosedLoopDisabled();
629 if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
630 "yes".equalsIgnoreCase(value) || "Y".equalsIgnoreCase(value)) {
631 throw new AAIException("is-closed-loop-disabled is set to true (query by vserver-name)");
636 public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
637 if ("true".equalsIgnoreCase(event.getAAI().get(VSERVER_IS_CLOSED_LOOP_DISABLED)) ||
638 "T".equalsIgnoreCase(event.getAAI().get(VSERVER_IS_CLOSED_LOOP_DISABLED)) ||
639 "yes".equalsIgnoreCase(event.getAAI().get(VSERVER_IS_CLOSED_LOOP_DISABLED)) ||
640 "Y".equalsIgnoreCase(event.getAAI().get(VSERVER_IS_CLOSED_LOOP_DISABLED))) {
643 return ("true".equalsIgnoreCase(event.getAAI().get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) ||
644 "T".equalsIgnoreCase(event.getAAI().get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) ||
645 "yes".equalsIgnoreCase(event.getAAI().get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) ||
646 "Y".equalsIgnoreCase(event.getAAI().get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)));
649 public static AAIGETVserverResponse getAAIVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
650 UUID requestID = event.getRequestID();
651 AAIGETVserverResponse response = null;
652 String vserverName = event.getAAI().get(VSERVER_VSERVER_NAME);
655 if (vserverName != null) {
656 aaiHostURL = PolicyEngine.manager.getEnvironmentProperty("aai.url");
657 aaiUser = PolicyEngine.manager.getEnvironmentProperty("aai.username");
658 aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
659 String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
660 String url = aaiHostURL + aaiGetQueryByVserver;
661 logger.info("AAI Host URL by VServer: {}", url);
662 response = new AAIManager(new RESTManager()).getQueryByVserverName(url, aaiUser, aaiPassword, requestID, vserverName);
664 } catch (Exception e) {
665 logger.error("getAAIVserverInfo exception: ", e);
666 throw new ControlLoopException("Exception in getAAIVserverInfo: ", e);
672 public static AAIGETVnfResponse getAAIVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
673 UUID requestID = event.getRequestID();
674 AAIGETVnfResponse response = null;
675 String vnfName = event.getAAI().get(GENERIC_VNF_VNF_NAME);
676 String vnfID = event.getAAI().get(GENERIC_VNF_VNF_ID);
678 aaiHostURL = PolicyEngine.manager.getEnvironmentProperty("aai.url");
679 aaiUser = PolicyEngine.manager.getEnvironmentProperty("aai.username");
680 aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
683 if (vnfName != null) {
684 String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
685 String url = aaiHostURL + aaiGetQueryByVnfName;
686 logger.info("AAI Host URL by VNF name: {}", url);
687 response = new AAIManager(new RESTManager()).getQueryByVnfName(url, aaiUser, aaiPassword, requestID, vnfName);
688 } else if (vnfID != null) {
689 String aaiGetQueryByVnfID = "/aai/v11/network/generic-vnfs/generic-vnf/";
690 String url = aaiHostURL + aaiGetQueryByVnfID;
691 logger.info("AAI Host URL by VNF ID: {}", url);
692 response = new AAIManager(new RESTManager()).getQueryByVnfID(url, aaiUser, aaiPassword, requestID, vnfID);
694 } catch (Exception e) {
695 logger.error("getAAIVnfInfo exception: ", e);
696 throw new ControlLoopException("Exception in getAAIVnfInfo: ", e);
703 public boolean isActive() {
709 public boolean releaseLock() {
715 public String toString() {
716 return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestID=" + requestID
717 + ", processor=" + processor + ", onset=" + (onset != null ? onset.getRequestID() : "null") + ", numOnsets=" + numOnsets + ", numAbatements="
718 + numAbatements + ", isActivated="
719 + isActivated + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";