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.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;
48 public class ControlLoopEventManager implements LockCallback, Serializable {
53 private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
55 private static final long serialVersionUID = -1216568161322872641L;
56 public final String closedLoopControlName;
57 public final UUID requestID;
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;
67 private boolean isActivated = false;
68 private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
69 private ControlLoopOperationManager currentOperation = null;
70 private TargetLock targetLock = null;
72 private static Collection<String> requiredAAIKeys = new ArrayList<>();
74 requiredAAIKeys.add("AICVServerSelfLink");
75 requiredAAIKeys.add("AICIdentity");
76 requiredAAIKeys.add("is_closed_loop_disabled");
77 requiredAAIKeys.add("VM_NAME");
80 public ControlLoopEventManager(String closedLoopControlName, UUID requestID) {
81 this.closedLoopControlName = closedLoopControlName;
82 this.requestID = requestID;
85 public String getControlLoopResult() {
86 return controlLoopResult;
89 public void setControlLoopResult(String controlLoopResult) {
90 this.controlLoopResult = controlLoopResult;
93 public Integer getNumOnsets() {
97 public void setNumOnsets(Integer numOnsets) {
98 this.numOnsets = numOnsets;
101 public Integer getNumAbatements() {
102 return numAbatements;
105 public void setNumAbatements(Integer numAbatements) {
106 this.numAbatements = numAbatements;
109 public boolean isActivated() {
113 public void setActivated(boolean isActivated) {
114 this.isActivated = isActivated;
117 public VirtualControlLoopEvent getOnsetEvent() {
121 public VirtualControlLoopEvent getAbatementEvent() {
122 return this.abatement;
125 public ControlLoopProcessor getProcessor() {
126 return this.processor;
129 public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
130 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
133 // This method should ONLY be called ONCE
135 if (this.isActivated) {
136 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
139 // Syntax check the event
141 checkEventSyntax(event);
143 // At this point we are good to go with this event
148 notification.notification = ControlLoopNotificationType.ACTIVE;
150 // Set ourselves as active
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();
163 public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
164 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
167 // This method should ONLY be called ONCE
169 if (this.isActivated) {
170 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
173 // Syntax check the event
175 checkEventSyntax(event);
180 if (yamlSpecification == null || yamlSpecification.length() < 1) {
181 throw new ControlLoopException("yaml specification is null or 0 length");
183 String decodedYaml = null;
185 decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
186 if (decodedYaml != null && decodedYaml.length() > 0) {
187 yamlSpecification = decodedYaml;
189 } catch (UnsupportedEncodingException e) {
190 logger.error("{}: activate threw: ",this, e);
193 // Parse the YAML specification
195 this.processor = new ControlLoopProcessor(yamlSpecification);
198 // At this point we are good to go with this event
205 notification.notification = ControlLoopNotificationType.ACTIVE;
207 // Set ourselves as active
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();
218 public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
220 // Check if they activated us
222 if (this.isActivated == false) {
223 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
226 // Make sure we are expecting this call.
228 if (this.onset == null) {
229 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
232 // Ok, start creating the notification
234 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
236 // Check if the overall control loop has timed out
238 if (this.isControlLoopTimedOut()) {
240 // Yes we have timed out
242 notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
243 notification.message = "Control Loop timed out";
244 notification.history.addAll(this.controlLoopHistory);
248 // Check if the current policy is Final
250 FinalResult result = this.processor.checkIsCurrentPolicyFinal();
251 if (result == null) {
253 // we are not at a final result
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;
267 notification.notification = ControlLoopNotificationType.FINAL_OPENLOOP;
270 notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
276 // Be sure to add all the history
278 notification.history.addAll(this.controlLoopHistory);
282 public ControlLoopOperationManager processControlLoop() throws ControlLoopException {
284 // Check if they activated us
286 if (this.isActivated == false) {
287 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
290 // Make sure we are expecting this call.
292 if (this.onset == null) {
293 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
296 // Is there a current operation?
298 if (this.currentOperation != null) {
300 // Throw an exception, or simply return the current operation?
302 throw new ControlLoopException("Already working an Operation, do not call this method.");
305 // Ensure we are not FINAL
307 VirtualControlLoopNotification notification = this.isControlLoopFinal();
308 if (notification != null) {
310 // This is weird, we require them to call the isControlLoopFinal() method first
312 // We should really abstract this and avoid throwing an exception, because it really
313 // isn't an exception.
315 throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
318 // Not final so get the policy that needs to be worked on.
320 Policy policy = this.processor.getCurrentPolicy();
321 if (policy == null) {
322 throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
325 // And setup an operation
327 this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
331 return this.currentOperation;
334 public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
336 // Verify we have a current operation
338 if (this.currentOperation != null) {
340 // Validate they are finishing the current operation
341 // PLD - this is simply comparing the policy. Do we want to equals the whole object?
343 if (this.currentOperation.policy.equals(operation.policy)) {
344 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(), this.currentOperation.getOperationResult());
348 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
350 // Move to the next Policy
352 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
354 // Just null this out
356 this.currentOperation = null;
358 // TODO: Release our lock
362 logger.debug("Cannot finish current operation {} does not match given operation {}", this.currentOperation.policy, operation.policy);
365 throw new ControlLoopException("No operation to finish.");
368 public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
372 if (this.currentOperation == null) {
373 throw new ControlLoopException("Do not have a current operation.");
376 // Have we acquired it already?
378 if (this.targetLock != null) {
380 // TODO: Make sure the current lock is for the same target.
381 // Currently, it should be. But in the future it may not.
383 return new LockResult<GuardResult, TargetLock>(GuardResult.LOCK_ACQUIRED, this.targetLock);
388 LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
389 this.currentOperation.policy.getTarget().getType(),
390 this.getTargetInstance(this.currentOperation.policy),
391 this.onset.requestID,
396 if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
398 // Yes, let's save it
400 this.targetLock = lockResult.getB();
406 public synchronized TargetLock unlockCurrentOperation() {
407 if (this.targetLock == null) {
410 if (PolicyGuard.unlockTarget(this.targetLock) == true) {
411 TargetLock returnLock = this.targetLock;
412 this.targetLock = null;
418 public enum NEW_EVENT_STATUS {
422 SUBSEQUENT_ABATEMENT,
427 public NEW_EVENT_STATUS onNewEvent(VirtualControlLoopEvent event) {
429 ControlLoopEventManager.checkEventSyntax(event);
430 if (event.closedLoopEventStatus == ControlLoopEventStatus.ONSET) {
432 // Check if this is our original ONSET
434 if (event.equals(this.onset)) {
438 return NEW_EVENT_STATUS.FIRST_ONSET;
441 // Log that we got an onset
444 return NEW_EVENT_STATUS.SUBSEQUENT_ONSET;
445 } else if (event.closedLoopEventStatus == ControlLoopEventStatus.ABATED) {
447 // Have we already got an abatement?
449 if (this.abatement == null) {
453 this.abatement = event;
455 // Keep track that we received another
457 this.numAbatements++;
461 return NEW_EVENT_STATUS.FIRST_ABATEMENT;
464 // Keep track that we received another
466 this.numAbatements++;
470 return NEW_EVENT_STATUS.SUBSEQUENT_ABATEMENT;
473 return NEW_EVENT_STATUS.SYNTAX_ERROR;
475 } catch (ControlLoopException e) {
476 logger.error("{}: onNewEvent threw: ",this, e);
477 return NEW_EVENT_STATUS.SYNTAX_ERROR;
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);
490 public boolean isControlLoopTimedOut() {
491 return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
494 public int getControlLoopTimeout(Integer defaultTimeout) {
495 if (this.processor != null && this.processor.getControlLoop() != null) {
496 return this.processor.getControlLoop().getTimeout();
498 if (defaultTimeout != null) {
499 return defaultTimeout;
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");
510 if (event.closedLoopControlName == null || event.closedLoopControlName.length() < 1) {
511 throw new ControlLoopException("No control loop name");
513 if (event.requestID == null) {
514 throw new ControlLoopException("No request ID");
516 if (event.AAI == null) {
517 throw new ControlLoopException("AAI is null");
519 if (event.AAI.get("vserver.is-closed-loop-disabled") == null) {
520 throw new ControlLoopException("vserver.is-closed-loop-disabled information missing");
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");
528 if (event.target == null || event.target.length() < 1) {
529 throw new ControlLoopException("No target field");
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");
541 public boolean isActive() {
547 public boolean releaseLock() {
552 public String getTargetInstance(Policy policy) {
553 if (policy.getTarget() != null) {
554 if (policy.getTarget().getType() != null) {
555 switch(policy.getTarget().getType()) {
559 if (this.onset.target.equalsIgnoreCase("vserver.vserver-name")) {
560 return this.onset.AAI.get("vserver.vserver-name");
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 + "]";