Skip to content

Commit 109f42f

Browse files
small refactoring and start implementing timeout
1 parent b71711b commit 109f42f

2 files changed

Lines changed: 26 additions & 26 deletions

File tree

plugins/iac/nimble/src/main/java/org/apache/cloudstack/service/NimbleService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public interface NimbleService extends PluggableService, Configurable {
3636
"tune this setting based on service utilization to optimize provisioning performance.",
3737
false, NimbleServiceEnabled.key());
3838

39+
ConfigKey<Integer> NimbleIaCTemplateExecutionTimeout = new ConfigKey<>("Advanced", Integer.class, "nimble.iac.template.execution.timeout", "600",
40+
"Timeout, in seconds, for the NIMBLE IaC templates to finish execution. Defaults to 600 seconds (10 minutes).",
41+
true, NimbleServiceEnabled.key());
42+
3943
ListResponse<IacResourceTypeResponse> listIacResourceTypes(ListIacResourceTypesCmd cmd);
4044
void deployIacTemplate(String iacTemplateContent, Map<String, String> inputs);
4145
}

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/orchestrator/ToscaOrchestrator.java

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.apache.cloudstack.jobs.JobInfo;
3939
import org.apache.cloudstack.managed.context.ManagedContextExecutor;
4040
import org.apache.cloudstack.persistence.iactemplatesprofile.IacResourceTypeVO;
41+
import org.apache.cloudstack.service.NimbleService;
4142
import org.apache.cloudstack.tosca.functions.ToscaFunction;
4243
import org.apache.cloudstack.tosca.model.ToscaInputDefinition;
4344
import org.apache.cloudstack.tosca.model.ToscaNodeTemplate;
@@ -65,9 +66,10 @@
6566
import java.util.Map;
6667
import java.util.Set;
6768
import java.util.concurrent.CompletableFuture;
68-
import java.util.concurrent.CompletionException;
6969
import java.util.concurrent.ExecutorService;
7070
import java.util.concurrent.Executors;
71+
import java.util.concurrent.TimeUnit;
72+
import java.util.concurrent.TimeoutException;
7173
import java.util.stream.Collectors;
7274

7375
public class ToscaOrchestrator {
@@ -99,10 +101,16 @@ public class ToscaOrchestrator {
99101
public void deployIacTemplate(String iacTemplateContent, Map<String, String> inputs) {
100102
ToscaServiceTemplate serviceTemplate = toscaParser.parseServiceTemplate(iacTemplateContent, toscaProfile, null);
101103
resolveServiceTemplateInputs(serviceTemplate, inputs);
102-
Map<String, CompletableFuture<String>> provisioningTasksFutures = createProvisioningTasksFutures(serviceTemplate);
104+
Map<String, CompletableFuture<Void>> provisioningTasksFutures = createProvisioningTasksFutures(serviceTemplate);
103105
logger.debug("Awaiting for all the provisioning tasks of the node template to complete.");
104-
CompletableFuture<Void> serviceTemplateFeature = CompletableFuture.allOf(provisioningTasksFutures.values().toArray(new CompletableFuture[0]));
105-
serviceTemplateFeature.join();
106+
int iacTemplateExecutionTimeout = NimbleService.NimbleIaCTemplateExecutionTimeout.value();
107+
CompletableFuture<Void> serviceTemplateFeature = CompletableFuture.allOf(provisioningTasksFutures.values().toArray(new CompletableFuture[0]))
108+
.orTimeout(iacTemplateExecutionTimeout, TimeUnit.SECONDS);
109+
try {
110+
serviceTemplateFeature.join();
111+
} catch (TimeoutException e) {
112+
113+
}
106114
logger.info("All provisioning tasks have completed successfully.");
107115
}
108116

@@ -145,24 +153,24 @@ private void handleGetInputFunctionCalls(String nodeTemplateName, ToscaProperty
145153
property.setEvaluatedValue(evaluatedValue);
146154
}
147155

148-
private Map<String, CompletableFuture<String>> createProvisioningTasksFutures(ToscaServiceTemplate serviceTemplate) {
156+
private Map<String, CompletableFuture<Void>> createProvisioningTasksFutures(ToscaServiceTemplate serviceTemplate) {
149157
logger.debug("Building provisioning tasks for the service template based on its graph topological sort.");
150-
Map<String, CompletableFuture<String>> futures = new HashMap<>();
158+
Map<String, CompletableFuture<Void>> futures = new HashMap<>();
151159
CallContext callContext = CallContext.current();
152160
getServiceTemplateTopologicalSort(serviceTemplate).forEach((node, dependencies) -> {
153161
ToscaNodeTemplate nodeTemplate = serviceTemplate.getNodeTemplates().get(node);
154-
CompletableFuture<String> taskFuture;
162+
CompletableFuture<Void> taskFuture;
155163
if (dependencies.isEmpty()) {
156164
logger.debug("Node [{}] has no dependencies. Building its provisioning task, which will be ready to be allocated for execution.", node);
157-
taskFuture = buildNodeProvisioningTask(nodeTemplate, callContext);
165+
taskFuture = provisionNode(nodeTemplate, callContext);
158166
} else {
159167
logger.debug("Node [{}] has [{}] dependencies. Building its provisioning task, which will only be allocated for execution when all dependencies are ready.", node, dependencies.size());
160168
CompletableFuture<?>[] dependenciesFutures = dependencies.stream()
161169
.map((dep) -> futures.get(dep.getName())).toArray(CompletableFuture[]::new);
162170
taskFuture = CompletableFuture.allOf(dependenciesFutures).thenCompose(v -> {
163171
logger.debug("All dependencies of the node [{}] are ready. Building its provisioning task.", node);
164172
executeGetAttributeAndGetPropertyFunctionCalls(nodeTemplate, serviceTemplate);
165-
return buildNodeProvisioningTask(nodeTemplate, callContext);
173+
return provisionNode(nodeTemplate, callContext);
166174
});
167175
}
168176

@@ -205,25 +213,12 @@ private void depthFirstSearch(LinkedHashMap<String, Set<ToscaNodeTemplate>> topo
205213
topologicalSort.put(node, graph.getOrDefault(node, Collections.emptySet()));
206214
}
207215

208-
private CompletableFuture<String> buildNodeProvisioningTask(ToscaNodeTemplate nodeTemplate, CallContext callContext) {
209-
return provisionNode(nodeTemplate, callContext)
210-
// .orTimeout(5, TimeUnit.SECONDS)
211-
.thenApply(result -> {
212-
logger.debug("SUCCESS [{}] [{}]", nodeTemplate.getName(), Thread.currentThread().getName());
213-
return result;
214-
}).exceptionally(ex -> {
215-
logger.debug("FAILURE [{}] [{}]", nodeTemplate.getName(), ex);
216-
throw new CompletionException(ex);
217-
});
218-
}
219-
220-
private CompletableFuture<String> provisionNode(ToscaNodeTemplate nodeTemplate, CallContext callContext) {
221-
return CompletableFuture.supplyAsync(() -> {
216+
private CompletableFuture<Void> provisionNode(ToscaNodeTemplate nodeTemplate, CallContext callContext) {
217+
return CompletableFuture.runAsync(() -> {
222218
CallContext.register(callContext, null);
223219
ManagedContextExecutor.execute(() -> {
224220
dispatchProvisioningCommand(nodeTemplate, callContext);
225221
});
226-
return "res-" + nodeTemplate.getName();
227222
}, executorPool);
228223
}
229224

@@ -242,8 +237,9 @@ private void dispatchProvisioningCommand(ToscaNodeTemplate nodeTemplate, CallCon
242237
logger.info("Result of the [{}] execution: {}.", nodeTemplate.getName(), provisioningResult);
243238
populateNodeTemplateAttributes(nodeTemplate, provisioningResult);
244239
} catch (Exception e) {
245-
logger.error("Could not instantiate the API class [{}]: {}.", apiClass.getName(), e.getMessage());
246-
throw new InvalidParameterValueException(String.format("Could not dispatch the provisioning task of [%s]. Please, check the availability of the API associated with it.", nodeTemplate.getName()));
240+
String errorMessage = String.format("Could execute the provisioning API [%s] of the node template [%s].", apiClass.getName(), nodeTemplate.getName());
241+
logger.error(errorMessage, e);
242+
throw new CloudRuntimeException(errorMessage, e);
247243
}
248244
}
249245

0 commit comments

Comments
 (0)