2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 Intel Corp. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright (c) 2019 Samsung
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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 * ============LICENSE_END=========================================================
23 package org.onap.so.openstack.utils;
25 import com.fasterxml.jackson.databind.JsonNode;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.woorea.openstack.heat.model.CreateStackParam;
28 import java.net.MalformedURLException;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.Scanner;
35 import javax.ws.rs.core.Response;
36 import javax.ws.rs.core.UriBuilderException;
37 import org.onap.so.adapters.vdu.CloudInfo;
38 import org.onap.so.adapters.vdu.PluginAction;
39 import org.onap.so.adapters.vdu.VduArtifact;
40 import org.onap.so.adapters.vdu.VduArtifact.ArtifactType;
41 import org.onap.so.adapters.vdu.VduException;
42 import org.onap.so.adapters.vdu.VduInstance;
43 import org.onap.so.adapters.vdu.VduModelInfo;
44 import org.onap.so.adapters.vdu.VduPlugin;
45 import org.onap.so.adapters.vdu.VduStateType;
46 import org.onap.so.adapters.vdu.VduStatus;
47 import org.onap.so.client.HttpClientFactory;
48 import org.onap.so.client.RestClient;
49 import org.onap.so.db.catalog.beans.CloudSite;
50 import org.onap.so.logger.ErrorCode;
51 import org.onap.so.logger.MessageEnum;
52 import org.onap.so.openstack.beans.HeatStatus;
53 import org.onap.so.openstack.beans.StackInfo;
54 import org.onap.so.openstack.exceptions.MsoAdapterException;
55 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
56 import org.onap.so.openstack.exceptions.MsoException;
57 import org.onap.so.openstack.exceptions.MsoOpenstackException;
58 import org.onap.so.utils.TargetEntity;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61 import org.springframework.beans.factory.annotation.Autowired;
62 import org.springframework.core.env.Environment;
63 import org.springframework.stereotype.Component;
66 public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin{
68 public static final String OOF_DIRECTIVES = "oof_directives";
69 public static final String SDNC_DIRECTIVES = "sdnc_directives";
70 public static final String VNF_ID = "vnf_id";
71 public static final String VF_MODULE_ID = "vf_module_id";
72 public static final String TEMPLATE_TYPE = "template_type";
73 public static final List<String> MULTICLOUD_INPUTS =
74 Arrays.asList(OOF_DIRECTIVES, SDNC_DIRECTIVES, TEMPLATE_TYPE);
76 private static final Logger logger = LoggerFactory.getLogger(MsoMulticloudUtils.class);
78 private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
79 private final HttpClientFactory httpClientFactory = new HttpClientFactory();
82 private Environment environment;
84 /******************************************************************************
86 * Methods (and associated utilities) to implement the VduPlugin interface
88 *******************************************************************************/
91 * Create a new Stack in the specified cloud location and tenant. The Heat template
92 * and parameter map are passed in as arguments, along with the cloud access credentials.
93 * It is expected that parameters have been validated and contain at minimum the required
94 * parameters for the given template with no extra (undefined) parameters..
96 * The Stack name supplied by the caller must be unique in the scope of this tenant.
97 * However, it should also be globally unique, as it will be the identifier for the
98 * resource going forward in Inventory. This latter is managed by the higher levels
99 * invoking this function.
101 * The caller may choose to let this function poll Openstack for completion of the
102 * stack creation, or may handle polling itself via separate calls to query the status.
103 * In either case, a StackInfo object will be returned containing the current status.
104 * When polling is enabled, a status of CREATED is expected. When not polling, a
105 * status of BUILDING is expected.
107 * An error will be thrown if the requested Stack already exists in the specified
110 * For 1510 - add "environment", "files" (nested templates), and "heatFiles" (get_files) as
111 * parameters for createStack. If environment is non-null, it will be added to the stack.
112 * The nested templates and get_file entries both end up being added to the "files" on the
113 * stack. We must combine them before we add them to the stack if they're both non-null.
115 * @param cloudSiteId The cloud (may be a region) in which to create the stack.
116 * @param tenantId The Openstack ID of the tenant in which to create the Stack
117 * @param stackName The name of the stack to create
118 * @param heatTemplate The Heat template
119 * @param stackInputs A map of key/value inputs
120 * @param pollForCompletion Indicator that polling should be handled in Java vs. in the client
121 * @param environment An optional yaml-format string to specify environmental parameters
122 * @param files a Map<String, Object> that lists the child template IDs (file is the string, object is an int of
124 * @param heatFiles a Map<String, Object> that lists the get_file entries (fileName, fileBody)
125 * @param backout Do not delete stack on create Failure - defaulted to True
126 * @return A StackInfo object
127 * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception.
130 @SuppressWarnings("unchecked")
132 public StackInfo createStack (String cloudSiteId,
136 Map <String, ?> stackInputs,
137 boolean pollForCompletion,
140 Map <String, Object> files,
141 Map <String, Object> heatFiles,
142 boolean backout) throws MsoException {
144 logger.trace("Started MsoMulticloudUtils.createStack");
146 // Get the directives, if present.
147 String oofDirectives = "{}";
148 String sdncDirectives = "{}";
149 String genericVnfId = "";
150 String vfModuleId = "";
151 String templateType = "";
153 for (String key: MULTICLOUD_INPUTS) {
154 if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) {
155 if (key == OOF_DIRECTIVES) {
156 oofDirectives = (String) stackInputs.get(key);
158 if (key == SDNC_DIRECTIVES) {
159 sdncDirectives = (String) stackInputs.get(key);
161 if (key == TEMPLATE_TYPE) {
162 templateType = (String) stackInputs.get(key);
164 if (logger.isDebugEnabled()) {
165 logger.debug(String.format("Found %s: %s", key, stackInputs.get(key)));
167 stackInputs.remove(key);
171 if (!stackInputs.isEmpty() && stackInputs.containsKey(VF_MODULE_ID)){
172 vfModuleId = (String) stackInputs.get(VF_MODULE_ID);
174 if (!stackInputs.isEmpty() && stackInputs.containsKey(VNF_ID)){
175 genericVnfId = (String) stackInputs.get(VNF_ID);
178 // create the multicloud payload
179 CreateStackParam stack = createStackParam(stackName, heatTemplate, stackInputs, timeoutMinutes, environment, files, heatFiles);
181 MulticloudRequest multicloudRequest= new MulticloudRequest();
183 multicloudRequest.setGenericVnfId(genericVnfId);
184 multicloudRequest.setVfModuleId(vfModuleId);
185 multicloudRequest.setTemplateType(templateType);
186 multicloudRequest.setTemplateData(stack);
187 multicloudRequest.setOofDirectives(getDirectiveNode(oofDirectives));
188 multicloudRequest.setSdncDirectives(getDirectiveNode(sdncDirectives));
189 if (logger.isDebugEnabled()) {
190 logger.debug(String.format("Multicloud Request is: %s", multicloudRequest.toString()));
193 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, null);
194 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint);
196 if (multicloudClient == null) {
197 MsoOpenstackException me = new MsoOpenstackException(0, "", "Multicloud client could not be initialized");
198 me.addContext(CREATE_STACK);
202 Response response = multicloudClient.post(multicloudRequest);
204 MulticloudCreateResponse multicloudResponseBody = null;
205 if (response.hasEntity()) {
206 multicloudResponseBody = getCreateBody((java.io.InputStream) response.getEntity());
208 if (response.getStatus() == Response.Status.CREATED.getStatusCode() && response.hasEntity()) {
209 String canonicalName = stackName + "/";
210 if (multicloudResponseBody != null) {
211 canonicalName = canonicalName + multicloudResponseBody.getWorkloadId();
213 if (logger.isDebugEnabled()) {
214 logger.debug("Multicloud Create Response Body: {}", multicloudResponseBody);
216 return getStackStatus(cloudSiteId, tenantId, canonicalName, pollForCompletion, timeoutMinutes, backout);
218 StringBuilder stackErrorStatusReason = new StringBuilder(response.getStatusInfo().getReasonPhrase());
219 if (null != multicloudResponseBody) {
220 stackErrorStatusReason.append(multicloudResponseBody.toString());
222 MsoOpenstackException me = new MsoOpenstackException(0, "", stackErrorStatusReason.toString());
223 me.addContext(CREATE_STACK);
228 public Map<String, Object> queryStackForOutputs(String cloudSiteId,
229 String tenantId, String stackName) throws MsoException {
230 logger.debug("MsoHeatUtils.queryStackForOutputs)");
231 StackInfo heatStack = this.queryStack(cloudSiteId, tenantId, stackName);
232 if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
235 return heatStack.getOutputs();
239 * Query for a single stack (by ID) in a tenant. This call will always return a
240 * StackInfo object. If the stack does not exist, an "empty" StackInfo will be
241 * returned - containing only the stack name and a status of NOTFOUND.
243 * @param tenantId The Openstack ID of the tenant in which to query
244 * @param cloudSiteId The cloud identifier (may be a region) in which to query
245 * @param stackId The ID of the stack to query
246 * @return A StackInfo object
247 * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception.
250 public StackInfo queryStack (String cloudSiteId, String tenantId, String instanceId) throws MsoException {
251 if (logger.isDebugEnabled()) {
252 logger.debug (String.format("Query multicloud HEAT stack: %s in tenant %s", instanceId, tenantId));
254 String stackName = null;
255 String stackId = null;
256 int offset = instanceId.indexOf('/');
257 if (offset > 0 && offset < (instanceId.length() - 1)) {
258 stackName = instanceId.substring(0, offset);
259 stackId = instanceId.substring(offset + 1);
261 stackName = instanceId;
262 stackId = instanceId;
265 StackInfo returnInfo = new StackInfo();
266 returnInfo.setName(stackName);
268 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, stackId);
269 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint);
271 if (multicloudClient != null) {
272 Response response = multicloudClient.get();
273 if (logger.isDebugEnabled()) {
274 logger.debug (String.format("Mulicloud GET Response: %s", response.toString()));
277 MulticloudQueryResponse multicloudQueryBody = null;
278 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
279 returnInfo.setStatus(HeatStatus.NOTFOUND);
280 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
281 } else if (response.getStatus() == Response.Status.OK.getStatusCode() && response.hasEntity()) {
282 multicloudQueryBody = getQueryBody((java.io.InputStream)response.getEntity());
283 returnInfo.setCanonicalName(stackName + "/" + multicloudQueryBody.getWorkloadId());
284 returnInfo.setStatus(getHeatStatus(multicloudQueryBody.getWorkloadStatus()));
285 returnInfo.setStatusMessage(multicloudQueryBody.getWorkloadStatus());
286 if (logger.isDebugEnabled()) {
287 logger.debug("Multicloud Create Response Body: " + multicloudQueryBody.toString());
290 returnInfo.setStatus(HeatStatus.FAILED);
291 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
298 public StackInfo deleteStack (String cloudSiteId, String tenantId, String instanceId) throws MsoException {
299 if (logger.isDebugEnabled()) {
300 logger.debug (String.format("Delete multicloud HEAT stack: %s in tenant %s", instanceId, tenantId));
302 String stackName = null;
303 String stackId = null;
304 int offset = instanceId.indexOf('/');
305 if (offset > 0 && offset < (instanceId.length() - 1)) {
306 stackName = instanceId.substring(0, offset);
307 stackId = instanceId.substring(offset + 1);
309 stackName = instanceId;
310 stackId = instanceId;
313 StackInfo returnInfo = new StackInfo();
314 returnInfo.setName(stackName);
315 Response response = null;
317 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, stackId);
318 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint);
320 if (multicloudClient != null) {
321 response = multicloudClient.delete();
322 if (logger.isDebugEnabled()) {
323 logger.debug(String.format("Multicloud Delete response is: %s", response.getEntity().toString()));
326 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
327 returnInfo.setStatus(HeatStatus.NOTFOUND);
328 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
329 } else if (response.getStatus() == Response.Status.NO_CONTENT.getStatusCode()) {
330 return getStackStatus(cloudSiteId, tenantId, instanceId);
332 returnInfo.setStatus(HeatStatus.FAILED);
333 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
337 returnInfo.setStatus(mapResponseToHeatStatus(response));
341 // ---------------------------------------------------------------
342 // PRIVATE FUNCTIONS FOR USE WITHIN THIS CLASS
344 private HeatStatus getHeatStatus(String workloadStatus) {
345 if (workloadStatus.length() == 0) return HeatStatus.INIT;
346 if ("CREATE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.BUILDING;
347 if ("CREATE_COMPLETE".equals(workloadStatus)) return HeatStatus.CREATED;
348 if ("CREATE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
349 if ("DELETE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.DELETING;
350 if ("DELETE_COMPLETE".equals(workloadStatus)) return HeatStatus.NOTFOUND;
351 if ("DELETE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
352 if ("UPDATE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.UPDATING;
353 if ("UPDATE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
354 if ("UPDATE_COMPLETE".equals(workloadStatus)) return HeatStatus.UPDATED;
355 return HeatStatus.UNKNOWN;
358 private StackInfo getStackStatus(String cloudSiteId, String tenantId, String instanceId) throws MsoException {
359 return getStackStatus(cloudSiteId, tenantId, instanceId, false, 0, false);
362 private StackInfo getStackStatus(String cloudSiteId, String tenantId, String instanceId, boolean pollForCompletion, int timeoutMinutes, boolean backout) throws MsoException {
363 StackInfo stackInfo = new StackInfo();
365 // If client has requested a final response, poll for stack completion
366 if (pollForCompletion) {
367 // Set a time limit on overall polling.
368 // Use the resource (template) timeout for Openstack (expressed in minutes)
369 // and add one poll interval to give Openstack a chance to fail on its own.s
371 int createPollInterval = Integer.parseInt(this.environment.getProperty(createPollIntervalProp, createPollIntervalDefault));
372 int pollTimeout = (timeoutMinutes * 60) + createPollInterval;
373 // New 1610 - poll on delete if we rollback - use same values for now
374 int deletePollInterval = createPollInterval;
375 int deletePollTimeout = pollTimeout;
376 boolean createTimedOut = false;
377 StringBuilder stackErrorStatusReason = new StringBuilder("");
378 logger.debug("createPollInterval=" + createPollInterval + ", pollTimeout=" + pollTimeout);
382 stackInfo = queryStack(cloudSiteId, tenantId, instanceId);
383 logger.debug (stackInfo.getStatus() + " (" + instanceId + ")");
385 if (HeatStatus.BUILDING.equals(stackInfo.getStatus())) {
386 // Stack creation is still running.
387 // Sleep and try again unless timeout has been reached
388 if (pollTimeout <= 0) {
389 // Note that this should not occur, since there is a timeout specified
390 // in the Openstack (multicloud?) call.
391 logger.error(String.format("%s %s %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT.toString(), cloudSiteId, tenantId, instanceId, stackInfo.getStatus(), "", "", ErrorCode.AvailabilityError.getValue(), "Create stack timeout"));
392 createTimedOut = true;
396 sleep(createPollInterval * 1000L);
398 pollTimeout -= createPollInterval;
399 logger.debug("pollTimeout remaining: " + pollTimeout);
401 //save off the status & reason msg before we attempt delete
402 stackErrorStatusReason.append("Stack error (" + stackInfo.getStatus() + "): " + stackInfo.getStatusMessage());
405 } catch (MsoException me) {
406 // Cannot query the stack status. Something is wrong.
407 // Try to roll back the stack
409 logger.warn(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack error, stack deletion suppressed", "", "", ErrorCode.BusinessProcesssError.getValue(), "Exception in Create Stack, stack deletion suppressed"));
412 logger.debug("Create Stack error - unable to query for stack status - attempting to delete stack: " + instanceId + " - This will likely fail and/or we won't be able to query to see if delete worked");
413 StackInfo deleteInfo = deleteStack(cloudSiteId, tenantId, instanceId);
414 // this may be a waste of time - if we just got an exception trying to query the stack - we'll just
415 // get another one, n'est-ce pas?
416 boolean deleted = false;
419 StackInfo queryInfo = queryStack(cloudSiteId, tenantId, instanceId);
420 logger.debug("Deleting " + instanceId + ", status: " + queryInfo.getStatus());
421 if (HeatStatus.DELETING.equals(queryInfo.getStatus())) {
422 if (deletePollTimeout <= 0) {
423 logger.error(String.format("%s %s %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT.toString(), cloudSiteId, tenantId, instanceId,
424 queryInfo.getStatus(), "", "", ErrorCode.AvailabilityError.getValue(),
425 "Rollback: DELETE stack timeout"));
428 sleep(deletePollInterval * 1000L);
429 deletePollTimeout -= deletePollInterval;
431 } else if (HeatStatus.NOTFOUND.equals(queryInfo.getStatus())){
432 logger.debug("DELETE_COMPLETE for " + instanceId);
436 //got a status other than DELETE_IN_PROGRESS or DELETE_COMPLETE - so break and evaluate
439 } catch (Exception e3) {
440 // Just log this one. We will report the original exception.
441 logger.error(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack: Nested exception rolling back stack: " + e3, "", "", ErrorCode.BusinessProcesssError.getValue(), "Create Stack: Nested exception rolling back stack on error on query"));
444 } catch (Exception e2) {
445 // Just log this one. We will report the original exception.
446 logger.error(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack: Nested exception rolling back stack: " + e2, "", "", ErrorCode.BusinessProcesssError.getValue(), "Create Stack: Nested exception rolling back stack"));
450 // Propagate the original exception from Stack Query.
451 me.addContext (CREATE_STACK);
456 if (!HeatStatus.CREATED.equals(stackInfo.getStatus())) {
457 logger.error(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack error: Polling complete with non-success status: "
458 + stackInfo.getStatus () + ", " + stackInfo.getStatusMessage(), "", "", ErrorCode.BusinessProcesssError.getValue(), "Create Stack error"));
460 // Rollback the stack creation, since it is in an indeterminate state.
462 logger.warn(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack errored, stack deletion suppressed", "", "", ErrorCode.BusinessProcesssError.getValue(), "Create Stack error, stack deletion suppressed"));
467 logger.debug("Create Stack errored - attempting to DELETE stack: " + instanceId);
468 logger.debug("deletePollInterval=" + deletePollInterval + ", deletePollTimeout=" + deletePollTimeout);
469 StackInfo deleteInfo = deleteStack(cloudSiteId, tenantId, instanceId);
470 boolean deleted = false;
473 StackInfo queryInfo = queryStack(cloudSiteId, tenantId, instanceId);
474 logger.debug("Deleting " + instanceId + ", status: " + queryInfo.getStatus());
475 if (HeatStatus.DELETING.equals(queryInfo.getStatus())) {
476 if (deletePollTimeout <= 0) {
477 logger.error(String.format("%s %s %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT.toString(), cloudSiteId, tenantId, instanceId,
478 queryInfo.getStatus(), "", "", ErrorCode.AvailabilityError.getValue(),
479 "Rollback: DELETE stack timeout"));
482 sleep(deletePollInterval * 1000L);
483 deletePollTimeout -= deletePollInterval;
485 } else if (HeatStatus.NOTFOUND.equals(queryInfo.getStatus())){
486 logger.debug("DELETE_COMPLETE for " + instanceId);
490 //got a status other than DELETE_IN_PROGRESS or DELETE_COMPLETE - so break and evaluate
491 logger.warn(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack errored, stack deletion FAILED", "", "", ErrorCode.BusinessProcesssError.getValue(), "Create Stack error, stack deletion FAILED"));
492 logger.debug("Stack deletion FAILED on a rollback of a create - " + instanceId + ", status=" + queryInfo.getStatus() + ", reason=" + queryInfo.getStatusMessage());
495 } catch (MsoException me2) {
496 // Just log this one. We will report the original exception.
497 logger.debug("Exception thrown trying to delete " + instanceId + " on a create->rollback: " + me2.getContextMessage(), me2);
498 logger.warn(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack errored, then stack deletion FAILED - exception thrown", "", "", ErrorCode.BusinessProcesssError.getValue(), me2.getContextMessage()));
501 StringBuilder errorContextMessage;
502 if (createTimedOut) {
503 errorContextMessage = new StringBuilder("Stack Creation Timeout");
505 errorContextMessage = stackErrorStatusReason;
508 errorContextMessage.append(" - stack successfully deleted");
510 errorContextMessage.append(" - encountered an error trying to delete the stack");
512 } catch (MsoException e2) {
513 // shouldn't happen - but handle
514 logger.error(String.format("%s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR.toString(), "Create Stack: Nested exception rolling back stack: " + e2, "", "", ErrorCode.BusinessProcesssError.getValue(), "Exception in Create Stack: rolling back stack"));
517 MsoOpenstackException me = new MsoOpenstackException(0, "", stackErrorStatusReason.toString());
518 me.addContext(CREATE_STACK);
522 // Get initial status, since it will have been null after the create.
523 stackInfo = queryStack(cloudSiteId, tenantId, instanceId);
524 logger.debug("Multicloud stack query status is: " + stackInfo.getStatus());
529 private HeatStatus mapResponseToHeatStatus(Response response) {
530 if (response.getStatusInfo().getStatusCode() == Response.Status.OK.getStatusCode()) {
531 return HeatStatus.CREATED;
532 } else if (response.getStatusInfo().getStatusCode() == Response.Status.CREATED.getStatusCode()) {
533 return HeatStatus.CREATED;
534 } else if (response.getStatusInfo().getStatusCode() == Response.Status.NO_CONTENT.getStatusCode()) {
535 return HeatStatus.CREATED;
536 } else if (response.getStatusInfo().getStatusCode() == Response.Status.BAD_REQUEST.getStatusCode()) {
537 return HeatStatus.FAILED;
538 } else if (response.getStatusInfo().getStatusCode() == Response.Status.UNAUTHORIZED.getStatusCode()) {
539 return HeatStatus.FAILED;
540 } else if (response.getStatusInfo().getStatusCode() == Response.Status.NOT_FOUND.getStatusCode()) {
541 return HeatStatus.NOTFOUND;
542 } else if (response.getStatusInfo().getStatusCode() == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) {
543 return HeatStatus.FAILED;
545 return HeatStatus.UNKNOWN;
549 private MulticloudCreateResponse getCreateBody(java.io.InputStream in) {
550 Scanner scanner = new Scanner(in);
551 scanner.useDelimiter("\\Z");
553 if (scanner.hasNext()) {
554 body = scanner.next();
559 return new ObjectMapper().readerFor(MulticloudCreateResponse.class).readValue(body);
560 } catch (Exception e) {
561 logger.debug("Exception retrieving multicloud vfModule POST response body " + e);
566 private MulticloudQueryResponse getQueryBody(java.io.InputStream in) {
567 Scanner scanner = new Scanner(in);
568 scanner.useDelimiter("\\Z");
570 if (scanner.hasNext()) {
571 body = scanner.next();
576 return new ObjectMapper().readerFor(MulticloudQueryResponse.class).readValue(body);
577 } catch (Exception e) {
578 logger.debug("Exception retrieving multicloud workload query response body " + e);
583 private String getMulticloudEndpoint(String cloudSiteId, String workloadId) throws MsoCloudSiteNotFound {
585 CloudSite cloudSite = cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
586 String endpoint = cloudSite.getIdentityService().getIdentityUrl();
588 if (workloadId != null) {
589 if (logger.isDebugEnabled()) {
590 logger.debug(String.format("Multicloud Endpoint is: %s/%s", endpoint, workloadId));
592 return String.format("%s/%s", endpoint, workloadId);
594 if (logger.isDebugEnabled()) {
595 logger.debug(String.format("Multicloud Endpoint is: %s", endpoint));
601 private RestClient getMulticloudClient(String endpoint) {
602 RestClient client = null;
604 client = httpClientFactory.newJsonClient(
606 TargetEntity.MULTICLOUD);
607 } catch (MalformedURLException e) {
608 logger.debug(String.format("Encountered malformed URL error getting multicloud rest client %s", e.getMessage()));
609 } catch (IllegalArgumentException e) {
610 logger.debug(String.format("Encountered illegal argument getting multicloud rest client %s",e.getMessage()));
611 } catch (UriBuilderException e) {
612 logger.debug(String.format("Encountered URI builder error getting multicloud rest client %s", e.getMessage()));
617 private JsonNode getDirectiveNode(String directives) throws MsoException {
619 return JSON_MAPPER.readTree(directives);
620 } catch (Exception e) {
621 logger.error(String.format("%s %s %s %s %d %s",
622 MessageEnum.RA_CREATE_STACK_ERR.toString(),
623 "Create Stack: " + e, "", "",
624 ErrorCode.BusinessProcesssError.getValue(),
625 "Exception in Create Stack: Invalid JSON format of directives" + directives));
626 MsoException me = new MsoAdapterException("Invalid JSON format of directives parameter: " + directives);
627 me.addContext(CREATE_STACK);
633 * VduPlugin interface for instantiate function.
635 * Translate the VduPlugin parameters to the corresponding 'createStack' parameters,
636 * and then invoke the existing function.
639 public VduInstance instantiateVdu (
642 Map<String,Object> inputs,
643 VduModelInfo vduModel,
644 boolean rollbackOnFailure)
647 String cloudSiteId = cloudInfo.getCloudSiteId();
648 String tenantId = cloudInfo.getTenantId();
650 // Translate the VDU ModelInformation structure to that which is needed for
651 // creating the Heat stack. Loop through the artifacts, looking specifically
652 // for MAIN_TEMPLATE and ENVIRONMENT. Any other artifact will
653 // be attached as a FILE.
654 String heatTemplate = null;
655 Map<String,Object> nestedTemplates = new HashMap<>();
656 Map<String,Object> files = new HashMap<>();
657 String heatEnvironment = null;
659 for (VduArtifact vduArtifact: vduModel.getArtifacts()) {
660 if (vduArtifact.getType() == ArtifactType.MAIN_TEMPLATE) {
661 heatTemplate = new String(vduArtifact.getContent());
663 else if (vduArtifact.getType() == ArtifactType.NESTED_TEMPLATE) {
664 nestedTemplates.put(vduArtifact.getName(), new String(vduArtifact.getContent()));
666 else if (vduArtifact.getType() == ArtifactType.ENVIRONMENT) {
667 heatEnvironment = new String(vduArtifact.getContent());
672 StackInfo stackInfo = createStack (cloudSiteId,
677 true, // poll for completion
678 vduModel.getTimeoutMinutes(),
683 // Populate a vduInstance from the StackInfo
684 return stackInfoToVduInstance(stackInfo);
686 catch (Exception e) {
687 throw new VduException ("MsoMulticloudUtils (instantiateVDU): createStack Exception", e);
693 * VduPlugin interface for query function.
696 public VduInstance queryVdu (CloudInfo cloudInfo, String instanceId)
699 String cloudSiteId = cloudInfo.getCloudSiteId();
700 String tenantId = cloudInfo.getTenantId();
703 // Query the Cloudify Deployment object and populate a VduInstance
704 StackInfo stackInfo = queryStack (cloudSiteId, tenantId, instanceId);
706 return stackInfoToVduInstance(stackInfo);
708 catch (Exception e) {
709 throw new VduException ("MsoMulticloudUtils (queryVdu): queryStack Exception ", e);
715 * VduPlugin interface for delete function.
718 public VduInstance deleteVdu (CloudInfo cloudInfo, String instanceId, int timeoutMinutes)
721 String cloudSiteId = cloudInfo.getCloudSiteId();
722 String tenantId = cloudInfo.getTenantId();
725 // Delete the Multicloud stack
726 StackInfo stackInfo = deleteStack (cloudSiteId, tenantId, instanceId);
728 // Populate a VduInstance based on the deleted Cloudify Deployment object
729 VduInstance vduInstance = stackInfoToVduInstance(stackInfo);
731 // Override return state to DELETED (MulticloudUtils sets to NOTFOUND)
732 vduInstance.getStatus().setState(VduStateType.DELETED);
736 catch (Exception e) {
737 throw new VduException ("Delete VDU Exception", e);
743 * VduPlugin interface for update function.
745 * Update is currently not supported in the MsoMulticloudUtils implementation of VduPlugin.
746 * Just return a VduException.
750 public VduInstance updateVdu (
753 Map<String,Object> inputs,
754 VduModelInfo vduModel,
755 boolean rollbackOnFailure)
758 throw new VduException ("MsoMulticloudUtils: updateVdu interface not supported");
763 * Convert the local DeploymentInfo object (Cloudify-specific) to a generic VduInstance object
765 protected VduInstance stackInfoToVduInstance (StackInfo stackInfo)
767 VduInstance vduInstance = new VduInstance();
769 if (logger.isDebugEnabled()) {
770 logger.debug(String.format("StackInfo to convert: %s", stackInfo.getParameters().toString()));
772 // The full canonical name as the instance UUID
773 vduInstance.setVduInstanceId(stackInfo.getCanonicalName());
774 vduInstance.setVduInstanceName(stackInfo.getName());
776 // Copy inputs and outputs
777 vduInstance.setInputs(stackInfo.getParameters());
778 vduInstance.setOutputs(stackInfo.getOutputs());
780 // Translate the status elements
781 vduInstance.setStatus(stackStatusToVduStatus (stackInfo));
786 private VduStatus stackStatusToVduStatus (StackInfo stackInfo)
788 VduStatus vduStatus = new VduStatus();
790 // Map the status fields to more generic VduStatus.
791 // There are lots of HeatStatus values, so this is a bit long...
792 HeatStatus heatStatus = stackInfo.getStatus();
793 String statusMessage = stackInfo.getStatusMessage();
794 logger.debug("HeatStatus = " + heatStatus + " msg = " + statusMessage);
796 if (logger.isDebugEnabled()) {
797 logger.debug(String.format("Stack Status: %s", heatStatus.toString()));
798 logger.debug(String.format("Stack Status Message: %s", statusMessage));
801 if (heatStatus == HeatStatus.INIT || heatStatus == HeatStatus.BUILDING) {
802 vduStatus.setState(VduStateType.INSTANTIATING);
803 vduStatus.setLastAction((new PluginAction ("create", "in_progress", statusMessage)));
805 else if (heatStatus == HeatStatus.NOTFOUND) {
806 vduStatus.setState(VduStateType.NOTFOUND);
808 else if (heatStatus == HeatStatus.CREATED) {
809 vduStatus.setState(VduStateType.INSTANTIATED);
810 vduStatus.setLastAction((new PluginAction ("create", "complete", statusMessage)));
812 else if (heatStatus == HeatStatus.UPDATED) {
813 vduStatus.setState(VduStateType.INSTANTIATED);
814 vduStatus.setLastAction((new PluginAction ("update", "complete", statusMessage)));
816 else if (heatStatus == HeatStatus.UPDATING) {
817 vduStatus.setState(VduStateType.UPDATING);
818 vduStatus.setLastAction((new PluginAction ("update", "in_progress", statusMessage)));
820 else if (heatStatus == HeatStatus.DELETING) {
821 vduStatus.setState(VduStateType.DELETING);
822 vduStatus.setLastAction((new PluginAction ("delete", "in_progress", statusMessage)));
824 else if (heatStatus == HeatStatus.FAILED) {
825 vduStatus.setState(VduStateType.FAILED);
826 vduStatus.setErrorMessage(stackInfo.getStatusMessage());
828 vduStatus.setState(VduStateType.UNKNOWN);