[APPC-144] OAM operation abort messages
[appc.git] / appc-oam / appc-oam-bundle / src / main / java / org / openecomp / appc / oam / processor / BaseActionRunnable.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.openecomp.appc.oam.processor;
26
27 import org.openecomp.appc.i18n.Msg;
28 import org.openecomp.appc.oam.AppcOam;
29 import org.openecomp.appc.oam.OAMCommandStatus;
30 import org.openecomp.appc.statemachine.impl.readers.AppcOamStates;
31
32 import java.util.HashMap;
33 import java.util.Map;
34 import java.util.concurrent.Future;
35
36 /**
37  * Base runnable actions for OAM APIs, such as maintenance mode, restart, start and stop API.
38  *
39  * <p>This class holds the general action async handling methods for all OAM APIs.
40  * <p>Specific API action runnable will overwrite the general methods to add specific behaviors.
41  *
42  * <p>Subclass constructor must set the following class variables:
43  *   <br>  - actionName
44  *   <br>  - auditMsg
45  *   <br>  - finalState
46  */
47 public abstract class BaseActionRunnable extends BaseCommon implements Runnable {
48     final String OAM_OPERATION_TIMEOUT_SECOND = "appc.OAM.api.timeout";
49     /** Default operation tiemout set to 1 minute */
50     final int DEFAULT_OAM_OPERATION_TIMEOUT = 60;
51     /** Abort due to rejection message format with flexible operation name */
52     final String ABORT_MESSAGE_FORMAT = "Aborting %s operation due to %s.";
53     /** Timeout message format with flexible operation name */
54     final String TIMEOUT_MESSAGE_FORMAT = "%s operation has reached timeout %d milliseconds.";
55     /** Failure message format with flexible number of bundles */
56     final String BUNDLE_OPERATION_FAILED_FORMAT = "%d bundle(s) failed, see logs for details.";
57     final String NEW_RPC_OPERATION_REQUEST = "new %s operation request";
58     final String DUE_TO_EXECUTION_ERROR = "due to execution error.";
59
60     private boolean isWaiting = false;
61     private AppcOamStates currentState;
62     long startTimeMs = 0;
63     long timeoutMs = 0;
64     boolean doTimeoutChecking = false;
65
66     String actionName = "Need to be reset";
67     Msg auditMsg;
68     AppcOamStates finalState;
69
70     BaseProcessor myParent;
71     Map<String, Future<?>> bundleNameToFuture = new HashMap<>();
72
73     /**
74      * Constructor
75      *
76      * @param parent BaseProcessor who has called this constructor.
77      */
78     BaseActionRunnable(BaseProcessor parent) {
79         super(parent.logger, parent.configurationHelper, parent.stateHelper, parent.operationHelper);
80
81         rpc = parent.rpc;
82         commonHeader = parent.commonHeader;
83         startTime = parent.startTime;
84         myParent = parent;
85
86         setTimeoutValues();
87     }
88
89     /**
90      * Set timeout in milliseconds
91      */
92     void setTimeoutValues() {
93         Integer timeoutSeconds = myParent.timeoutSeconds;
94         if (timeoutSeconds == null) {
95             timeoutMs = configurationHelper.getConfig().getIntegerProperty(
96                     OAM_OPERATION_TIMEOUT_SECOND, DEFAULT_OAM_OPERATION_TIMEOUT) * 1000;
97         } else {
98             timeoutMs = timeoutSeconds.longValue() * 1000;
99         }
100
101         doTimeoutChecking = timeoutMs != 0;
102         if (doTimeoutChecking) {
103             startTimeMs = startTime.getTime();
104         }
105         logDebug("%s action runnable check timeout (%s) with timeout (%d)ms, and startMs (%d)",
106                 rpc.name(), Boolean.toString(doTimeoutChecking), timeoutMs, startTimeMs);
107     }
108
109     /**
110      * Abort operation handling due to outside interruption, does<br>
111      *     - set ABORT status<br>
112      *     - send notification message<br>
113      *     - add audit log
114      *
115      * @param newRpc of the new AppcOam.RPC operation.
116      */
117     public void abortRunnable(final AppcOam.RPC newRpc) {
118         resetLogProperties(false);
119
120         String additionalMsg = String.format(NEW_RPC_OPERATION_REQUEST, newRpc);
121         logDebug("%s action aborted due to %s", rpc.name(), additionalMsg);
122         setStatus(OAMCommandStatus.ABORT, String.format(ABORT_MESSAGE_FORMAT, rpc.name(), additionalMsg));
123         operationHelper.sendNotificationMessage(rpc, commonHeader, status);
124         auditInfoLog(auditMsg);
125
126         resetLogProperties(true);
127     }
128
129     @Override
130     public void run() {
131         try {
132             setInitialLogProperties();
133             logDebug(String.format("===========in %s run (waiting: %s)=======",
134                     actionName, Boolean.toString(isWaiting)));
135
136             if (isWaiting) {
137                 if (!checkState()) {
138                     keepWaiting();
139                 }
140             } else {
141                 if (doAction()) {
142                     isWaiting = !checkState();
143                 } else {
144                     postDoAction(false);
145                 }
146             }
147         } catch (Exception e) {
148             logDebug(String.format("%s got exception %s", actionName, e.getMessage()));
149             logger.error(actionName + " exception", e);
150
151         } finally {
152             clearRequestLogProperties();
153         }
154     }
155
156     /**
157      * Keep waiting to be override by children classes for different behaviors.
158      * Timeout is validated here.
159      */
160     void keepWaiting() {
161         logDebug(String.format("%s runnable waiting, current state is %s.",
162                 actionName, currentState == null ? "null" : currentState.toString()));
163
164         isTimeout("keepWaiting");
165     }
166
167     /**
168      * Check if the timeout milliseconds has reached.
169      *
170      * @param parentName String of the caller, for logging purpose.
171      * @return true if the timeout has reached, otherwise false.
172      */
173     boolean isTimeout(String parentName) {
174         logDebug(String.format("%s task isTimeout called from %s", actionName, parentName));
175         if (doTimeoutChecking
176                 && System.currentTimeMillis() - startTimeMs > timeoutMs) {
177             logger.error(String.format("%s operation timeout (%d) ms has reached, abort with error state.",
178                     actionName, timeoutMs));
179
180             setStatus(OAMCommandStatus.TIMEOUT, String.format(TIMEOUT_MESSAGE_FORMAT, rpc.name(), timeoutMs));
181             postAction(AppcOamStates.Error);
182             return true;
183         }
184         return false;
185     }
186
187     /**
188      * Check if all bundle operations are successful through BundleHelper.
189      * If there's failed bundler operation, set error status and trigger postAction with Error state.
190      *
191      * @return true if bundler operations have failure, otherwise false.
192      */
193     boolean hasBundleOperationFailure() {
194         long failedTask = myParent.bundleHelper.getFailedMetrics(bundleNameToFuture);
195         if (failedTask == 0) {
196             return false;
197         }
198
199         setStatus(OAMCommandStatus.UNEXPECTED_ERROR, String.format(BUNDLE_OPERATION_FAILED_FORMAT, failedTask));
200         postAction(AppcOamStates.Error);
201         return true;
202     }
203
204     /**
205      * Set class <b>status</b> to ABORT with abort message.
206      */
207     void setAbortStatus() {
208         setStatus(OAMCommandStatus.ABORT, String.format(ABORT_MESSAGE_FORMAT, rpc.name(), DUE_TO_EXECUTION_ERROR));
209     }
210
211     /**
212      * Final handling. The thread is cancelled.
213      *
214      * @param setState boolean to indicate if set OAM state or not
215      */
216     void postDoAction(boolean setState) {
217         logDebug(String.format("Finished %s task", actionName));
218     }
219
220     /**
221      * Handling for after doAction. does post notification, issue audit log and set OAM state based on input.
222      *
223      * @param state of AppcOamState to be set as OAM state when it is not null.
224      */
225     void postAction(AppcOamStates state) {
226         operationHelper.sendNotificationMessage(rpc, commonHeader, status);
227
228         if (state != null) {
229             stateHelper.setState(state);
230         }
231
232         auditInfoLog(auditMsg);
233
234         myParent.cancelAsyncTask();
235     }
236
237     /**
238      * Check state
239      *
240      * @return true if final state reached, otherwise return false
241      */
242     boolean checkState() {
243         if (isTimeout("checkState")) {
244             myParent.bundleHelper.cancelUnfinished(bundleNameToFuture);
245             return true;
246         }
247
248         if (!myParent.bundleHelper.isAllTaskDone(bundleNameToFuture)) {
249             return false;
250         }
251
252         if (hasBundleOperationFailure()) {
253             return true;
254         }
255
256         currentState = stateHelper.getBundlesState();
257         if (currentState == finalState) {
258             setStatus(OAMCommandStatus.SUCCESS);
259             postDoAction(true);
260             return true;
261         }
262         return false;
263     }
264
265     abstract boolean doAction();
266 }