2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 Intel Corp. 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.so.openstack.utils;
23 import java.net.MalformedURLException;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Scanner;
30 import javax.ws.rs.core.UriBuilder;
31 import javax.ws.rs.core.UriBuilderException;
32 import javax.ws.rs.core.Response;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.onap.so.adapters.vdu.CloudInfo;
37 import org.onap.so.adapters.vdu.PluginAction;
38 import org.onap.so.adapters.vdu.VduArtifact;
39 import org.onap.so.adapters.vdu.VduArtifact.ArtifactType;
40 import org.onap.so.adapters.vdu.VduException;
41 import org.onap.so.adapters.vdu.VduInstance;
42 import org.onap.so.adapters.vdu.VduModelInfo;
43 import org.onap.so.adapters.vdu.VduPlugin;
44 import org.onap.so.adapters.vdu.VduStateType;
45 import org.onap.so.adapters.vdu.VduStatus;
46 import org.onap.so.openstack.beans.HeatStatus;
47 import org.onap.so.openstack.beans.StackInfo;
48 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
49 import org.onap.so.openstack.exceptions.MsoException;
50 import org.onap.so.openstack.exceptions.MsoOpenstackException;
51 import org.onap.so.client.HttpClient;
52 import org.onap.so.client.RestClient;
53 import org.onap.so.db.catalog.beans.CloudSite;
54 import org.onap.so.logger.MessageEnum;
55 import org.onap.so.logger.MsoAlarmLogger;
56 import org.onap.so.logger.MsoLogger;
57 import org.onap.so.utils.TargetEntity;
58 import org.springframework.beans.factory.annotation.Autowired;
59 import org.springframework.core.env.Environment;
60 import org.springframework.http.HttpEntity;
61 import org.springframework.http.HttpHeaders;
62 import org.springframework.http.MediaType;
63 import org.springframework.stereotype.Component;
65 import com.fasterxml.jackson.core.JsonProcessingException;
66 import com.fasterxml.jackson.databind.JsonNode;
67 import com.fasterxml.jackson.databind.ObjectMapper;
68 import com.woorea.openstack.heat.model.CreateStackParam;
71 public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin{
73 public static final String OOF_DIRECTIVES = "oof_directives";
74 public static final String SDNC_DIRECTIVES = "sdnc_directives";
75 public static final String GENERIC_VNF_ID = "generic_vnf_id";
76 public static final String VF_MODULE_ID = "vf_module_id";
77 public static final String TEMPLATE_TYPE = "template_type";
78 public static final List<String> MULTICLOUD_INPUTS =
79 Arrays.asList(OOF_DIRECTIVES, SDNC_DIRECTIVES, GENERIC_VNF_ID, VF_MODULE_ID, TEMPLATE_TYPE);
81 private static final Logger logger = LoggerFactory.getLogger(MsoMulticloudUtils.class);
83 private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
86 private Environment environment;
89 /******************************************************************************
91 * Methods (and associated utilities) to implement the VduPlugin interface
93 *******************************************************************************/
96 * Create a new Stack in the specified cloud location and tenant. The Heat template
97 * and parameter map are passed in as arguments, along with the cloud access credentials.
98 * It is expected that parameters have been validated and contain at minimum the required
99 * parameters for the given template with no extra (undefined) parameters..
101 * The Stack name supplied by the caller must be unique in the scope of this tenant.
102 * However, it should also be globally unique, as it will be the identifier for the
103 * resource going forward in Inventory. This latter is managed by the higher levels
104 * invoking this function.
106 * The caller may choose to let this function poll Openstack for completion of the
107 * stack creation, or may handle polling itself via separate calls to query the status.
108 * In either case, a StackInfo object will be returned containing the current status.
109 * When polling is enabled, a status of CREATED is expected. When not polling, a
110 * status of BUILDING is expected.
112 * An error will be thrown if the requested Stack already exists in the specified
115 * For 1510 - add "environment", "files" (nested templates), and "heatFiles" (get_files) as
116 * parameters for createStack. If environment is non-null, it will be added to the stack.
117 * The nested templates and get_file entries both end up being added to the "files" on the
118 * stack. We must combine them before we add them to the stack if they're both non-null.
120 * @param cloudSiteId The cloud (may be a region) in which to create the stack.
121 * @param tenantId The Openstack ID of the tenant in which to create the Stack
122 * @param stackName The name of the stack to create
123 * @param heatTemplate The Heat template
124 * @param stackInputs A map of key/value inputs
125 * @param pollForCompletion Indicator that polling should be handled in Java vs. in the client
126 * @param environment An optional yaml-format string to specify environmental parameters
127 * @param files a Map<String, Object> that lists the child template IDs (file is the string, object is an int of
129 * @param heatFiles a Map<String, Object> that lists the get_file entries (fileName, fileBody)
130 * @param backout Do not delete stack on create Failure - defaulted to True
131 * @return A StackInfo object
132 * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception.
135 @SuppressWarnings("unchecked")
137 public StackInfo createStack (String cloudSiteId,
141 Map <String, ?> stackInputs,
142 boolean pollForCompletion,
145 Map <String, Object> files,
146 Map <String, Object> heatFiles,
147 boolean backout) throws MsoException {
149 logger.trace("Started MsoMulticloudUtils.createStack");
151 // Get the directives, if present.
152 String oofDirectives = "";
153 String sdncDirectives = "";
154 String genericVnfId = "";
155 String vfModuleId = "";
156 String templateType = "";
158 for (String key: MULTICLOUD_INPUTS) {
159 if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) {
160 if ( key == OOF_DIRECTIVES) {oofDirectives = (String) stackInputs.get(key);}
161 if ( key == SDNC_DIRECTIVES) {sdncDirectives = (String) stackInputs.get(key);}
162 if ( key == GENERIC_VNF_ID) {genericVnfId = (String) stackInputs.get(key);}
163 if ( key == VF_MODULE_ID) {vfModuleId = (String) stackInputs.get(key);}
164 if ( key == TEMPLATE_TYPE) {templateType = (String) stackInputs.get(key);}
165 if (logger.isDebugEnabled()) {
166 logger.debug(String.format("Found %s: %s", key, stackInputs.get(key)));
168 stackInputs.remove(key);
172 // create the multicloud payload
173 CreateStackParam stack = createStackParam(stackName, heatTemplate, stackInputs, timeoutMinutes, environment, files, heatFiles);
175 MulticloudRequest multicloudRequest= new MulticloudRequest();
178 multicloudRequest.setGenericVnfId(genericVnfId);
179 multicloudRequest.setVfModuleId(vfModuleId);
180 multicloudRequest.setOofDirectives(JSON_MAPPER.readTree(oofDirectives));
181 multicloudRequest.setSdncDirectives(JSON_MAPPER.readTree(sdncDirectives));
182 multicloudRequest.setTemplateType(templateType);
183 if (logger.isDebugEnabled()) {
184 logger.debug(String.format("Stack Template Data is: %s", stack.toString().substring(16)));
186 multicloudRequest.setTemplateData(stack);
187 if (logger.isDebugEnabled()) {
188 logger.debug(String.format("Multicloud Request is: %s", multicloudRequest.toString()));
190 } catch (Exception e) {
191 logger.debug("ERROR making multicloud JSON body ", e);
193 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, null);
194 if (logger.isDebugEnabled()) {
195 logger.debug(String.format("Multicloud Endpoint is: %s", multicloudEndpoint));
197 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint);
199 Response response = multicloudClient.post(multicloudRequest);
201 StackInfo createInfo = new StackInfo();
202 createInfo.setName(stackName);
204 MulticloudCreateResponse multicloudResponseBody = null;
205 if (response.getStatus() == Response.Status.CREATED.getStatusCode() && response.hasEntity()) {
206 multicloudResponseBody = getCreateBody((java.io.InputStream)response.getEntity());
207 createInfo.setCanonicalName(stackName + "/" + multicloudResponseBody.getWorkloadId());
208 if (logger.isDebugEnabled()) {
209 logger.debug("Multicloud Create Response Body: " + multicloudResponseBody);
211 return getStackStatus(cloudSiteId, tenantId, multicloudResponseBody.getWorkloadId(), pollForCompletion, timeoutMinutes, backout);
213 createInfo.setStatus(HeatStatus.FAILED);
214 createInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
220 public Map<String, Object> queryStackForOutputs(String cloudSiteId,
221 String tenantId, String stackName) throws MsoException {
222 logger.debug("MsoHeatUtils.queryStackForOutputs)");
223 StackInfo heatStack = this.queryStack(cloudSiteId, tenantId, stackName);
224 if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
227 return heatStack.getOutputs();
231 * Query for a single stack (by ID) in a tenant. This call will always return a
232 * StackInfo object. If the stack does not exist, an "empty" StackInfo will be
233 * returned - containing only the stack name and a status of NOTFOUND.
235 * @param tenantId The Openstack ID of the tenant in which to query
236 * @param cloudSiteId The cloud identifier (may be a region) in which to query
237 * @param stackId The ID of the stack to query
238 * @return A StackInfo object
239 * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception.
242 public StackInfo queryStack (String cloudSiteId, String tenantId, String instanceId) throws MsoException {
243 if (logger.isDebugEnabled()) {
244 logger.debug (String.format("Query multicloud HEAT stack: %s in tenant %s", instanceId, tenantId));
246 String stackName = null;
247 String stackId = null;
248 int offset = instanceId.indexOf('/');
249 if (offset > 0 && offset < (instanceId.length() - 1)) {
250 stackName = instanceId.substring(0, offset);
251 stackId = instanceId.substring(offset + 1);
253 stackName = instanceId;
254 stackId = instanceId;
257 StackInfo returnInfo = new StackInfo();
258 returnInfo.setName(stackName);
260 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, stackId);
261 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint);
263 if (multicloudClient != null) {
264 Response response = multicloudClient.get();
265 if (logger.isDebugEnabled()) {
266 logger.debug (String.format("Mulicloud GET Response: %s", response.toString()));
269 MulticloudQueryResponse multicloudQueryBody = null;
270 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
271 returnInfo.setStatus(HeatStatus.NOTFOUND);
272 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
273 } else if (response.getStatus() == Response.Status.OK.getStatusCode() && response.hasEntity()) {
274 multicloudQueryBody = getQueryBody((java.io.InputStream)response.getEntity());
275 returnInfo.setCanonicalName(stackName + "/" + multicloudQueryBody.getWorkloadId());
276 returnInfo.setStatus(getHeatStatus(multicloudQueryBody.getWorkloadStatus()));
277 returnInfo.setStatusMessage(multicloudQueryBody.getWorkloadStatus());
278 if (logger.isDebugEnabled()) {
279 logger.debug("Multicloud Create Response Body: " + multicloudQueryBody.toString());
282 returnInfo.setStatus(HeatStatus.FAILED);
283 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
290 public StackInfo deleteStack (String cloudSiteId, String tenantId, String instanceId) throws MsoException {
291 if (logger.isDebugEnabled()) {
292 logger.debug (String.format("Delete multicloud HEAT stack: %s in tenant %s", instanceId, tenantId));
294 String stackName = null;
295 String stackId = null;
296 int offset = instanceId.indexOf('/');
297 if (offset > 0 && offset < (instanceId.length() - 1)) {
298 stackName = instanceId.substring(0, offset);
299 stackId = instanceId.substring(offset + 1);
301 stackName = instanceId;
302 stackId = instanceId;
305 StackInfo returnInfo = new StackInfo();
306 returnInfo.setName(stackName);
307 Response response = null;
309 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, stackId);
310 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint);
312 if (multicloudClient != null) {
313 response = multicloudClient.delete();
314 if (logger.isDebugEnabled()) {
315 logger.debug(String.format("Multicloud Delete response is: %s", response.getEntity().toString()));
318 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
319 returnInfo.setStatus(HeatStatus.NOTFOUND);
320 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
321 } else if (response.getStatus() == Response.Status.NO_CONTENT.getStatusCode()) {
322 return getStackStatus(cloudSiteId, tenantId, instanceId);
324 returnInfo.setStatus(HeatStatus.FAILED);
325 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
329 returnInfo.setStatus(mapResponseToHeatStatus(response));
333 // ---------------------------------------------------------------
334 // PRIVATE FUNCTIONS FOR USE WITHIN THIS CLASS
336 private HeatStatus getHeatStatus(String workloadStatus) {
337 if (workloadStatus.length() == 0) return HeatStatus.INIT;
338 if ("CREATE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.BUILDING;
339 if ("CREATE_COMPLETE".equals(workloadStatus)) return HeatStatus.CREATED;
340 if ("CREATE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
341 if ("DELETE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.DELETING;
342 if ("DELETE_COMPLETE".equals(workloadStatus)) return HeatStatus.NOTFOUND;
343 if ("DELETE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
344 if ("UPDATE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.UPDATING;
345 if ("UPDATE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
346 if ("UPDATE_COMPLETE".equals(workloadStatus)) return HeatStatus.UPDATED;
347 return HeatStatus.UNKNOWN;
350 private StackInfo getStackStatus(String cloudSiteId, String tenantId, String instanceId) throws MsoException {
351 return getStackStatus(cloudSiteId, tenantId, instanceId, false, 0, false);
354 private StackInfo getStackStatus(String cloudSiteId, String tenantId, String instanceId, boolean pollForCompletion, int timeoutMinutes, boolean backout) throws MsoException {
355 StackInfo stackInfo = new StackInfo();
357 // If client has requested a final response, poll for stack completion
358 if (pollForCompletion) {
359 // Set a time limit on overall polling.
360 // Use the resource (template) timeout for Openstack (expressed in minutes)
361 // and add one poll interval to give Openstack a chance to fail on its own.s
363 int createPollInterval = Integer.parseInt(this.environment.getProperty(createPollIntervalProp, createPollIntervalDefault));
364 int pollTimeout = (timeoutMinutes * 60) + createPollInterval;
365 // New 1610 - poll on delete if we rollback - use same values for now
366 int deletePollInterval = createPollInterval;
367 int deletePollTimeout = pollTimeout;
368 boolean createTimedOut = false;
369 StringBuilder stackErrorStatusReason = new StringBuilder("");
370 logger.debug("createPollInterval=" + createPollInterval + ", pollTimeout=" + pollTimeout);
374 stackInfo = queryStack(cloudSiteId, tenantId, instanceId);
375 logger.debug (stackInfo.getStatus() + " (" + instanceId + ")");
377 if (HeatStatus.BUILDING.equals(stackInfo.getStatus())) {
378 // Stack creation is still running.
379 // Sleep and try again unless timeout has been reached
380 if (pollTimeout <= 0) {
381 // Note that this should not occur, since there is a timeout specified
382 // in the Openstack (multicloud?) call.
383 logger.error(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT, cloudSiteId, tenantId, instanceId, stackInfo.getStatus(), "", "", MsoLogger.ErrorCode.AvailabilityError, "Create stack timeout"));
384 createTimedOut = true;
388 sleep(createPollInterval * 1000L);
390 pollTimeout -= createPollInterval;
391 logger.debug("pollTimeout remaining: " + pollTimeout);
393 //save off the status & reason msg before we attempt delete
394 stackErrorStatusReason.append("Stack error (" + stackInfo.getStatus() + "): " + stackInfo.getStatusMessage());
397 } catch (MsoException me) {
398 // Cannot query the stack status. Something is wrong.
399 // Try to roll back the stack
401 logger.warn(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack errored, stack deletion suppressed", "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception in Create Stack, stack deletion suppressed"));
404 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");
405 StackInfo deleteInfo = deleteStack(cloudSiteId, tenantId, instanceId);
406 // this may be a waste of time - if we just got an exception trying to query the stack - we'll just
407 // get another one, n'est-ce pas?
408 boolean deleted = false;
411 StackInfo queryInfo = queryStack(cloudSiteId, tenantId, instanceId);
412 logger.debug("Deleting " + instanceId + ", status: " + queryInfo.getStatus());
413 if (HeatStatus.DELETING.equals(queryInfo.getStatus())) {
414 if (deletePollTimeout <= 0) {
415 logger.error(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT, cloudSiteId, tenantId, instanceId,
416 queryInfo.getStatus(), "", "", MsoLogger.ErrorCode.AvailabilityError,
417 "Rollback: DELETE stack timeout"));
420 sleep(deletePollInterval * 1000L);
421 deletePollTimeout -= deletePollInterval;
423 } else if (HeatStatus.NOTFOUND.equals(queryInfo.getStatus())){
424 logger.debug("DELETE_COMPLETE for " + instanceId);
428 //got a status other than DELETE_IN_PROGRESS or DELETE_COMPLETE - so break and evaluate
431 } catch (Exception e3) {
432 // Just log this one. We will report the original exception.
433 logger.error(String.format("%d %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack: Nested exception rolling back stack: " + e3, "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create Stack: Nested exception rolling back stack on error on query"));
436 } catch (Exception e2) {
437 // Just log this one. We will report the original exception.
438 logger.error(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack: Nested exception rolling back stack: " + e2, "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create Stack: Nested exception rolling back stack"));
442 // Propagate the original exception from Stack Query.
443 me.addContext (CREATE_STACK);
448 if (!HeatStatus.CREATED.equals(stackInfo.getStatus())) {
449 logger.error(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack error: Polling complete with non-success status: "
450 + stackInfo.getStatus () + ", " + stackInfo.getStatusMessage(), "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create Stack error"));
452 // Rollback the stack creation, since it is in an indeterminate state.
454 logger.warn(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack errored, stack deletion suppressed", "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create Stack error, stack deletion suppressed"));
459 logger.debug("Create Stack errored - attempting to DELETE stack: " + instanceId);
460 logger.debug("deletePollInterval=" + deletePollInterval + ", deletePollTimeout=" + deletePollTimeout);
461 StackInfo deleteInfo = deleteStack(cloudSiteId, tenantId, instanceId);
462 boolean deleted = false;
465 StackInfo queryInfo = queryStack(cloudSiteId, tenantId, instanceId);
466 logger.debug("Deleting " + instanceId + ", status: " + queryInfo.getStatus());
467 if (HeatStatus.DELETING.equals(queryInfo.getStatus())) {
468 if (deletePollTimeout <= 0) {
469 logger.error(String.format("%d %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT, cloudSiteId, tenantId, instanceId,
470 queryInfo.getStatus(), "", "", MsoLogger.ErrorCode.AvailabilityError,
471 "Rollback: DELETE stack timeout"));
474 sleep(deletePollInterval * 1000L);
475 deletePollTimeout -= deletePollInterval;
477 } else if (HeatStatus.NOTFOUND.equals(queryInfo.getStatus())){
478 logger.debug("DELETE_COMPLETE for " + instanceId);
482 //got a status other than DELETE_IN_PROGRESS or DELETE_COMPLETE - so break and evaluate
483 logger.warn(String.format("%d %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack errored, stack deletion FAILED", "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Create Stack error, stack deletion FAILED"));
484 logger.debug("Stack deletion FAILED on a rollback of a create - " + instanceId + ", status=" + queryInfo.getStatus() + ", reason=" + queryInfo.getStatusMessage());
487 } catch (MsoException me2) {
488 // Just log this one. We will report the original exception.
489 logger.debug("Exception thrown trying to delete " + instanceId + " on a create->rollback: " + me2.getContextMessage(), me2);
490 logger.warn(String.format("%d %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack errored, then stack deletion FAILED - exception thrown", "", "", MsoLogger.ErrorCode.BusinessProcesssError, me2.getContextMessage()));
493 StringBuilder errorContextMessage;
494 if (createTimedOut) {
495 errorContextMessage = new StringBuilder("Stack Creation Timeout");
497 errorContextMessage = stackErrorStatusReason;
500 errorContextMessage.append(" - stack successfully deleted");
502 errorContextMessage.append(" - encountered an error trying to delete the stack");
504 } catch (MsoException e2) {
505 // shouldn't happen - but handle
506 logger.error(String.format("%d %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_ERR, "Create Stack: Nested exception rolling back stack: " + e2, "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception in Create Stack: rolling back stack"));
509 MsoOpenstackException me = new MsoOpenstackException(0, "", stackErrorStatusReason.toString());
510 me.addContext(CREATE_STACK);
511 alarmLogger.sendAlarm(HEAT_ERROR, MsoAlarmLogger.CRITICAL, me.getContextMessage());
515 // Get initial status, since it will have been null after the create.
516 stackInfo = queryStack(cloudSiteId, tenantId, instanceId);
517 logger.debug("Multicloud stack query status is: " + stackInfo.getStatus());
522 private HeatStatus mapResponseToHeatStatus(Response response) {
523 if (response.getStatusInfo().getStatusCode() == Response.Status.OK.getStatusCode()) {
524 return HeatStatus.CREATED;
525 } else if (response.getStatusInfo().getStatusCode() == Response.Status.CREATED.getStatusCode()) {
526 return HeatStatus.CREATED;
527 } else if (response.getStatusInfo().getStatusCode() == Response.Status.NO_CONTENT.getStatusCode()) {
528 return HeatStatus.CREATED;
529 } else if (response.getStatusInfo().getStatusCode() == Response.Status.BAD_REQUEST.getStatusCode()) {
530 return HeatStatus.FAILED;
531 } else if (response.getStatusInfo().getStatusCode() == Response.Status.UNAUTHORIZED.getStatusCode()) {
532 return HeatStatus.FAILED;
533 } else if (response.getStatusInfo().getStatusCode() == Response.Status.NOT_FOUND.getStatusCode()) {
534 return HeatStatus.NOTFOUND;
535 } else if (response.getStatusInfo().getStatusCode() == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) {
536 return HeatStatus.FAILED;
538 return HeatStatus.UNKNOWN;
542 private MulticloudCreateResponse getCreateBody(java.io.InputStream in) {
543 Scanner scanner = new Scanner(in);
544 scanner.useDelimiter("\\Z");
546 if (scanner.hasNext()) {
547 body = scanner.next();
552 return new ObjectMapper().readerFor(MulticloudCreateResponse.class).readValue(body);
553 } catch (Exception e) {
554 logger.debug("Exception retrieving multicloud vfModule POST response body " + e);
559 private MulticloudQueryResponse getQueryBody(java.io.InputStream in) {
560 Scanner scanner = new Scanner(in);
561 scanner.useDelimiter("\\Z");
563 if (scanner.hasNext()) {
564 body = scanner.next();
569 return new ObjectMapper().readerFor(MulticloudQueryResponse.class).readValue(body);
570 } catch (Exception e) {
571 logger.debug("Exception retrieving multicloud workload query response body " + e);
576 private String getMulticloudEndpoint(String cloudSiteId, String workloadId) throws MsoCloudSiteNotFound {
578 CloudSite cloudSite = cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
579 String endpoint = cloudSite.getIdentityService().getIdentityUrl();
581 if (workloadId != null) {
582 if (logger.isDebugEnabled()) {
583 logger.debug(String.format("Multicloud Endpoint is: %s/%s", endpoint, workloadId));
585 return String.format("%s/%s", endpoint, workloadId);
587 if (logger.isDebugEnabled()) {
588 logger.debug(String.format("Multicloud Endpoint is: %s", endpoint));
594 private RestClient getMulticloudClient(String endpoint) {
595 RestClient client = null;
597 client = new HttpClient(UriBuilder.fromUri(endpoint).build().toURL(),
598 MediaType.APPLICATION_JSON.toString(), TargetEntity.MULTICLOUD);
599 } catch (MalformedURLException e) {
600 logger.debug(String.format("Encountered malformed URL error getting multicloud rest client %s", e.getMessage()));
601 } catch (IllegalArgumentException e) {
602 logger.debug(String.format("Encountered illegal argument getting multicloud rest client %s",e.getMessage()));
603 } catch (UriBuilderException e) {
604 logger.debug(String.format("Encountered URI builder error getting multicloud rest client %s", e.getMessage()));
610 * VduPlugin interface for instantiate function.
612 * Translate the VduPlugin parameters to the corresponding 'createStack' parameters,
613 * and then invoke the existing function.
616 public VduInstance instantiateVdu (
619 Map<String,Object> inputs,
620 VduModelInfo vduModel,
621 boolean rollbackOnFailure)
624 String cloudSiteId = cloudInfo.getCloudSiteId();
625 String tenantId = cloudInfo.getTenantId();
627 // Translate the VDU ModelInformation structure to that which is needed for
628 // creating the Heat stack. Loop through the artifacts, looking specifically
629 // for MAIN_TEMPLATE and ENVIRONMENT. Any other artifact will
630 // be attached as a FILE.
631 String heatTemplate = null;
632 Map<String,Object> nestedTemplates = new HashMap<>();
633 Map<String,Object> files = new HashMap<>();
634 String heatEnvironment = null;
636 for (VduArtifact vduArtifact: vduModel.getArtifacts()) {
637 if (vduArtifact.getType() == ArtifactType.MAIN_TEMPLATE) {
638 heatTemplate = new String(vduArtifact.getContent());
640 else if (vduArtifact.getType() == ArtifactType.NESTED_TEMPLATE) {
641 nestedTemplates.put(vduArtifact.getName(), new String(vduArtifact.getContent()));
643 else if (vduArtifact.getType() == ArtifactType.ENVIRONMENT) {
644 heatEnvironment = new String(vduArtifact.getContent());
649 StackInfo stackInfo = createStack (cloudSiteId,
654 true, // poll for completion
655 vduModel.getTimeoutMinutes(),
660 // Populate a vduInstance from the StackInfo
661 return stackInfoToVduInstance(stackInfo);
663 catch (Exception e) {
664 throw new VduException ("MsoMulticloudUtils (instantiateVDU): createStack Exception", e);
670 * VduPlugin interface for query function.
673 public VduInstance queryVdu (CloudInfo cloudInfo, String instanceId)
676 String cloudSiteId = cloudInfo.getCloudSiteId();
677 String tenantId = cloudInfo.getTenantId();
680 // Query the Cloudify Deployment object and populate a VduInstance
681 StackInfo stackInfo = queryStack (cloudSiteId, tenantId, instanceId);
683 return stackInfoToVduInstance(stackInfo);
685 catch (Exception e) {
686 throw new VduException ("MsoMulticloudUtils (queryVdu): queryStack Exception ", e);
692 * VduPlugin interface for delete function.
695 public VduInstance deleteVdu (CloudInfo cloudInfo, String instanceId, int timeoutMinutes)
698 String cloudSiteId = cloudInfo.getCloudSiteId();
699 String tenantId = cloudInfo.getTenantId();
702 // Delete the Multicloud stack
703 StackInfo stackInfo = deleteStack (cloudSiteId, tenantId, instanceId);
705 // Populate a VduInstance based on the deleted Cloudify Deployment object
706 VduInstance vduInstance = stackInfoToVduInstance(stackInfo);
708 // Override return state to DELETED (MulticloudUtils sets to NOTFOUND)
709 vduInstance.getStatus().setState(VduStateType.DELETED);
713 catch (Exception e) {
714 throw new VduException ("Delete VDU Exception", e);
720 * VduPlugin interface for update function.
722 * Update is currently not supported in the MsoMulticloudUtils implementation of VduPlugin.
723 * Just return a VduException.
727 public VduInstance updateVdu (
730 Map<String,Object> inputs,
731 VduModelInfo vduModel,
732 boolean rollbackOnFailure)
735 throw new VduException ("MsoMulticloudUtils: updateVdu interface not supported");
740 * Convert the local DeploymentInfo object (Cloudify-specific) to a generic VduInstance object
742 protected VduInstance stackInfoToVduInstance (StackInfo stackInfo)
744 VduInstance vduInstance = new VduInstance();
746 if (logger.isDebugEnabled()) {
747 logger.debug(String.format("StackInfo to convert: %s", stackInfo.getParameters().toString()));
749 // The full canonical name as the instance UUID
750 vduInstance.setVduInstanceId(stackInfo.getCanonicalName());
751 vduInstance.setVduInstanceName(stackInfo.getName());
753 // Copy inputs and outputs
754 vduInstance.setInputs(stackInfo.getParameters());
755 vduInstance.setOutputs(stackInfo.getOutputs());
757 // Translate the status elements
758 vduInstance.setStatus(stackStatusToVduStatus (stackInfo));
763 private VduStatus stackStatusToVduStatus (StackInfo stackInfo)
765 VduStatus vduStatus = new VduStatus();
767 // Map the status fields to more generic VduStatus.
768 // There are lots of HeatStatus values, so this is a bit long...
769 HeatStatus heatStatus = stackInfo.getStatus();
770 String statusMessage = stackInfo.getStatusMessage();
771 logger.debug("HeatStatus = " + heatStatus + " msg = " + statusMessage);
773 if (logger.isDebugEnabled()) {
774 logger.debug(String.format("Stack Status: %s", heatStatus.toString()));
775 logger.debug(String.format("Stack Status Message: %s", statusMessage));
778 if (heatStatus == HeatStatus.INIT || heatStatus == HeatStatus.BUILDING) {
779 vduStatus.setState(VduStateType.INSTANTIATING);
780 vduStatus.setLastAction((new PluginAction ("create", "in_progress", statusMessage)));
782 else if (heatStatus == HeatStatus.NOTFOUND) {
783 vduStatus.setState(VduStateType.NOTFOUND);
785 else if (heatStatus == HeatStatus.CREATED) {
786 vduStatus.setState(VduStateType.INSTANTIATED);
787 vduStatus.setLastAction((new PluginAction ("create", "complete", statusMessage)));
789 else if (heatStatus == HeatStatus.UPDATED) {
790 vduStatus.setState(VduStateType.INSTANTIATED);
791 vduStatus.setLastAction((new PluginAction ("update", "complete", statusMessage)));
793 else if (heatStatus == HeatStatus.UPDATING) {
794 vduStatus.setState(VduStateType.UPDATING);
795 vduStatus.setLastAction((new PluginAction ("update", "in_progress", statusMessage)));
797 else if (heatStatus == HeatStatus.DELETING) {
798 vduStatus.setState(VduStateType.DELETING);
799 vduStatus.setLastAction((new PluginAction ("delete", "in_progress", statusMessage)));
801 else if (heatStatus == HeatStatus.FAILED) {
802 vduStatus.setState(VduStateType.FAILED);
803 vduStatus.setErrorMessage(stackInfo.getStatusMessage());
805 vduStatus.setState(VduStateType.UNKNOWN);