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.UriBuilder;
37 import javax.ws.rs.core.UriBuilderException;
38 import org.onap.so.adapters.vdu.CloudInfo;
39 import org.onap.so.adapters.vdu.PluginAction;
40 import org.onap.so.adapters.vdu.VduArtifact;
41 import org.onap.so.adapters.vdu.VduArtifact.ArtifactType;
42 import org.onap.so.adapters.vdu.VduException;
43 import org.onap.so.adapters.vdu.VduInstance;
44 import org.onap.so.adapters.vdu.VduModelInfo;
45 import org.onap.so.adapters.vdu.VduPlugin;
46 import org.onap.so.adapters.vdu.VduStateType;
47 import org.onap.so.adapters.vdu.VduStatus;
48 import org.onap.so.client.HttpClient;
49 import org.onap.so.client.HttpClientFactory;
50 import org.onap.so.client.RestClient;
51 import org.onap.so.logger.ErrorCode;
52 import org.onap.so.logger.MessageEnum;
53 import org.onap.so.openstack.beans.HeatStatus;
54 import org.onap.so.openstack.beans.StackInfo;
55 import org.onap.so.openstack.exceptions.MsoAdapterException;
56 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
57 import org.onap.so.openstack.exceptions.MsoException;
58 import org.onap.so.openstack.exceptions.MsoOpenstackException;
59 import org.onap.so.utils.TargetEntity;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62 import org.springframework.beans.factory.annotation.Autowired;
63 import org.springframework.core.env.Environment;
64 import org.springframework.stereotype.Component;
67 public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin{
69 public static final String OOF_DIRECTIVES = "oof_directives";
70 public static final String SDNC_DIRECTIVES = "sdnc_directives";
71 public static final String USER_DIRECTIVES = "user_directives";
72 public static final String VNF_ID = "vnf_id";
73 public static final String VF_MODULE_ID = "vf_module_id";
74 public static final String TEMPLATE_TYPE = "template_type";
75 public static final String MULTICLOUD_QUERY_BODY_NULL = "multicloudQueryBody is null";
76 public static final List<String> MULTICLOUD_INPUTS =
77 Arrays.asList(OOF_DIRECTIVES, SDNC_DIRECTIVES, USER_DIRECTIVES, TEMPLATE_TYPE);
79 private static final Logger logger = LoggerFactory.getLogger(MsoMulticloudUtils.class);
81 private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
82 private static final Integer DEFAULT_MSB_PORT = 80;
83 private static final String DEFAULT_MSB_IP = "127.0.0.1";
84 private static final String ONAP_IP = "ONAP_IP";
85 private final HttpClientFactory httpClientFactory = new HttpClientFactory();
88 private Environment environment;
90 /******************************************************************************
92 * Methods (and associated utilities) to implement the VduPlugin interface
94 *******************************************************************************/
97 * Create a new Stack in the specified cloud location and tenant. The Heat template
98 * and parameter map are passed in as arguments, along with the cloud access credentials.
99 * It is expected that parameters have been validated and contain at minimum the required
100 * parameters for the given template with no extra (undefined) parameters..
102 * The Stack name supplied by the caller must be unique in the scope of this tenant.
103 * However, it should also be globally unique, as it will be the identifier for the
104 * resource going forward in Inventory. This latter is managed by the higher levels
105 * invoking this function.
107 * The caller may choose to let this function poll Openstack for completion of the
108 * stack creation, or may handle polling itself via separate calls to query the status.
109 * In either case, a StackInfo object will be returned containing the current status.
110 * When polling is enabled, a status of CREATED is expected. When not polling, a
111 * status of BUILDING is expected.
113 * An error will be thrown if the requested Stack already exists in the specified
116 * For 1510 - add "environment", "files" (nested templates), and "heatFiles" (get_files) as
117 * parameters for createStack. If environment is non-null, it will be added to the stack.
118 * The nested templates and get_file entries both end up being added to the "files" on the
119 * stack. We must combine them before we add them to the stack if they're both non-null.
121 * @param cloudSiteId The cloud (may be a region) in which to create the stack
122 * @param cloudOwner the cloud owner of the cloud site in which to create the stack
123 * @param tenantId The Openstack ID of the tenant in which to create the Stack
124 * @param stackName The name of the stack to create
125 * @param heatTemplate The Heat template
126 * @param stackInputs A map of key/value inputs
127 * @param pollForCompletion Indicator that polling should be handled in Java vs. in the client
128 * @param environment An optional yaml-format string to specify environmental parameters
129 * @param files a Map<String, Object> that lists the child template IDs (file is the string, object is an int of
131 * @param heatFiles a Map<String, Object> that lists the get_file entries (fileName, fileBody)
132 * @param backout Do not delete stack on create Failure - defaulted to True
133 * @return A StackInfo object
134 * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception.
137 @SuppressWarnings("unchecked")
139 public StackInfo createStack (String cloudSiteId,
143 VduModelInfo vduModel,
145 Map <String, ?> stackInputs,
146 boolean pollForCompletion,
149 Map <String, Object> files,
150 Map <String, Object> heatFiles,
151 boolean backout) throws MsoException {
153 logger.trace("Started MsoMulticloudUtils.createStack");
155 // Get the directives, if present.
156 String oofDirectives = "{}";
157 String sdncDirectives = "{}";
158 String userDirectives = "{}";
159 String genericVnfId = "";
160 String vfModuleId = "";
161 String templateType = "";
163 for (String key: MULTICLOUD_INPUTS) {
164 if (!stackInputs.isEmpty() && stackInputs.containsKey(key)) {
165 if (key == OOF_DIRECTIVES) {
166 oofDirectives = (String) stackInputs.get(key);
168 if (key == SDNC_DIRECTIVES) {
169 sdncDirectives = (String) stackInputs.get(key);
171 if (key == USER_DIRECTIVES) {
172 sdncDirectives = (String) stackInputs.get(key);
174 if (key == TEMPLATE_TYPE) {
175 templateType = (String) stackInputs.get(key);
177 if (logger.isDebugEnabled()) {
178 logger.debug(String.format("Found %s: %s", key, stackInputs.get(key)));
180 stackInputs.remove(key);
184 if (!stackInputs.isEmpty() && stackInputs.containsKey(VF_MODULE_ID)){
185 vfModuleId = (String) stackInputs.get(VF_MODULE_ID);
187 if (!stackInputs.isEmpty() && stackInputs.containsKey(VNF_ID)){
188 genericVnfId = (String) stackInputs.get(VNF_ID);
191 // create the multicloud payload
192 CreateStackParam stack = createStackParam(stackName, heatTemplate, stackInputs, timeoutMinutes, environment, files, heatFiles);
194 MulticloudRequest multicloudRequest= new MulticloudRequest();
196 multicloudRequest.setGenericVnfId(genericVnfId);
197 multicloudRequest.setVfModuleId(vfModuleId);
198 multicloudRequest.setVfModuleModelInvariantId(vduModel.getModelInvariantUUID());
199 multicloudRequest.setVfModuleModelVersionId(vduModel.getModelUUID());
200 multicloudRequest.setVfModuleModelCustomizationId(vduModel.getModelCustomizationUUID());
201 multicloudRequest.setTemplateType(templateType);
202 multicloudRequest.setTemplateData(stack);
203 multicloudRequest.setOofDirectives(getDirectiveNode(oofDirectives));
204 multicloudRequest.setSdncDirectives(getDirectiveNode(sdncDirectives));
205 multicloudRequest.setUserDirectives(getDirectiveNode(userDirectives));
206 if (logger.isDebugEnabled()) {
207 logger.debug(String.format("Multicloud Request is: %s", multicloudRequest.toString()));
210 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, null);
211 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId);
213 if (multicloudClient == null) {
214 MsoOpenstackException me = new MsoOpenstackException(0, "", "Multicloud client could not be initialized");
215 me.addContext(CREATE_STACK);
219 Response response = multicloudClient.post(multicloudRequest);
221 MulticloudCreateResponse multicloudResponseBody = null;
222 if (response.hasEntity()) {
223 multicloudResponseBody = getCreateBody((java.io.InputStream) response.getEntity());
225 if (response.getStatus() == Response.Status.CREATED.getStatusCode() && response.hasEntity()) {
226 String canonicalName = stackName + "/";
227 if (multicloudResponseBody != null) {
228 canonicalName = canonicalName + multicloudResponseBody.getWorkloadId();
230 if (logger.isDebugEnabled()) {
231 logger.debug("Multicloud Create Response Body: {}", multicloudResponseBody);
233 StackInfo stackStatus = getStackStatus(cloudSiteId, cloudOwner, tenantId, canonicalName, pollForCompletion, timeoutMinutes, backout);
235 if (HeatStatus.CREATED.equals(stackStatus.getStatus())) {
236 String workloadId = multicloudResponseBody == null ? null : multicloudResponseBody.getWorkloadId();
237 multicloudAaiUpdate(cloudSiteId, cloudOwner, tenantId, genericVnfId, vfModuleId, workloadId, pollForCompletion, timeoutMinutes);
242 StringBuilder stackErrorStatusReason = new StringBuilder(response.getStatusInfo().getReasonPhrase());
243 if (null != multicloudResponseBody) {
244 stackErrorStatusReason.append(multicloudResponseBody.toString());
246 MsoOpenstackException me = new MsoOpenstackException(0, "", stackErrorStatusReason.toString());
247 me.addContext(CREATE_STACK);
252 public Map<String, Object> queryStackForOutputs(String cloudSiteId, String cloudOwner,
253 String tenantId, String stackName) throws MsoException {
254 logger.debug("MsoHeatUtils.queryStackForOutputs)");
255 StackInfo heatStack = this.queryStack(cloudSiteId, cloudOwner, tenantId, stackName);
256 if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
259 return heatStack.getOutputs();
263 * Query for a single stack (by ID) in a tenant. This call will always return a
264 * StackInfo object. If the stack does not exist, an "empty" StackInfo will be
265 * returned - containing only the stack name and a status of NOTFOUND.
267 * @param tenantId The Openstack ID of the tenant in which to query
268 * @param cloudSiteId The cloud identifier (may be a region) in which to query
269 * @param cloudOwner cloud owner of the cloud site in which to query
270 * @param stackId The ID of the stack to query
271 * @return A StackInfo object
272 * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception.
275 public StackInfo queryStack (String cloudSiteId, String cloudOwner, String tenantId, String instanceId) throws MsoException {
276 if (logger.isDebugEnabled()) {
277 logger.debug (String.format("Query multicloud HEAT stack: %s in tenant %s", instanceId, tenantId));
279 String stackName = null;
280 String stackId = null;
281 int offset = instanceId.indexOf('/');
282 if (offset > 0 && offset < (instanceId.length() - 1)) {
283 stackName = instanceId.substring(0, offset);
284 stackId = instanceId.substring(offset + 1);
286 stackName = instanceId;
287 stackId = instanceId;
290 StackInfo returnInfo = new StackInfo();
291 returnInfo.setName(stackName);
293 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId);
294 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId);
296 if (multicloudClient != null) {
297 Response response = multicloudClient.get();
298 if (logger.isDebugEnabled()) {
299 logger.debug (String.format("Multicloud GET Response: %s", response.toString()));
302 MulticloudQueryResponse multicloudQueryBody = null;
303 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
304 returnInfo.setStatus(HeatStatus.NOTFOUND);
305 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
306 } else if (response.getStatus() == Response.Status.OK.getStatusCode() && response.hasEntity()) {
307 multicloudQueryBody = getQueryBody((java.io.InputStream)response.getEntity());
308 if (multicloudQueryBody != null) {
309 returnInfo.setCanonicalName(stackName + "/" + multicloudQueryBody.getWorkloadId());
310 returnInfo.setStatus(getHeatStatus(multicloudQueryBody.getWorkloadStatus()));
311 returnInfo.setStatusMessage(multicloudQueryBody.getWorkloadStatus());
312 if (logger.isDebugEnabled()) {
313 logger.debug("Multicloud Create Response Body: " + multicloudQueryBody.toString());
316 returnInfo.setStatus(HeatStatus.FAILED);
317 returnInfo.setStatusMessage(MULTICLOUD_QUERY_BODY_NULL);
320 returnInfo.setStatus(HeatStatus.FAILED);
321 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
328 public StackInfo deleteStack (String cloudSiteId, String cloudOwner, String tenantId, String instanceId) throws MsoException {
329 if (logger.isDebugEnabled()) {
330 logger.debug (String.format("Delete multicloud HEAT stack: %s in tenant %s", instanceId, tenantId));
332 String stackName = null;
333 String stackId = null;
334 int offset = instanceId.indexOf('/');
335 if (offset > 0 && offset < (instanceId.length() - 1)) {
336 stackName = instanceId.substring(0, offset);
337 stackId = instanceId.substring(offset + 1);
339 stackName = instanceId;
340 stackId = instanceId;
343 StackInfo returnInfo = new StackInfo();
344 returnInfo.setName(stackName);
345 Response response = null;
347 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId);
348 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId);
350 if (multicloudClient != null) {
351 response = multicloudClient.delete();
352 if (logger.isDebugEnabled()) {
353 logger.debug(String.format("Multicloud Delete response is: %s", response.getEntity().toString()));
356 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
357 returnInfo.setStatus(HeatStatus.NOTFOUND);
358 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
359 } else if (response.getStatus() == Response.Status.NO_CONTENT.getStatusCode()) {
360 return getStackStatus(cloudSiteId, cloudOwner, tenantId, instanceId);
362 returnInfo.setStatus(HeatStatus.FAILED);
363 returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase());
367 returnInfo.setStatus(mapResponseToHeatStatus(response));
371 // ---------------------------------------------------------------
372 // PRIVATE FUNCTIONS FOR USE WITHIN THIS CLASS
374 private HeatStatus getHeatStatus(String workloadStatus) {
375 if (workloadStatus.length() == 0) return HeatStatus.INIT;
376 if ("CREATE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.BUILDING;
377 if ("CREATE_COMPLETE".equals(workloadStatus)) return HeatStatus.CREATED;
378 if ("CREATE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
379 if ("DELETE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.DELETING;
380 if ("DELETE_COMPLETE".equals(workloadStatus)) return HeatStatus.NOTFOUND;
381 if ("DELETE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
382 if ("UPDATE_IN_PROGRESS".equals(workloadStatus)) return HeatStatus.UPDATING;
383 if ("UPDATE_FAILED".equals(workloadStatus)) return HeatStatus.FAILED;
384 if ("UPDATE_COMPLETE".equals(workloadStatus)) return HeatStatus.UPDATED;
385 return HeatStatus.UNKNOWN;
388 private void multicloudAaiUpdate(String cloudSiteId, String cloudOwner, String tenantId, String genericVnfId, String vfModuleId, String workloadId,
389 boolean pollForCompletion, int timeoutMinutes) {
391 MulticloudRequest multicloudRequest= new MulticloudRequest();
393 multicloudRequest.setGenericVnfId(genericVnfId);
394 multicloudRequest.setVfModuleId(vfModuleId);
396 String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, workloadId);
397 RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId);
399 if (multicloudClient == null) {
400 if (logger.isDebugEnabled())
401 logger.debug("Multicloud client could not be initialized");
405 Response response = multicloudClient.post(multicloudRequest);
406 if (response.getStatus() != Response.Status.ACCEPTED.getStatusCode()) {
407 if (logger.isDebugEnabled())
408 logger.debug("Multicloud AAI update request failed: " + response.getStatus() + response.getStatusInfo());
412 if (!pollForCompletion) {
416 int updatePollInterval = Integer.parseInt(this.environment.getProperty(createPollIntervalProp, createPollIntervalDefault));
417 int pollTimeout = (timeoutMinutes * 60) + updatePollInterval;
418 boolean updateTimedOut = false;
419 logger.debug("updatePollInterval=" + updatePollInterval + ", pollTimeout=" + pollTimeout);
421 StackInfo stackInfo = null;
424 stackInfo = queryStack(cloudSiteId, cloudOwner, tenantId, workloadId);
425 if (logger.isDebugEnabled())
426 logger.debug (stackInfo.getStatus() + " (" + workloadId + ")");
428 if (HeatStatus.UPDATING.equals(stackInfo.getStatus())) {
429 if (pollTimeout <= 0) {
430 // Note that this should not occur, since there is a timeout specified
431 // in the Openstack (multicloud?) call.
432 if (logger.isDebugEnabled())
433 logger.debug("Multicloud AAI update timeout failure: {} {} {} {}", cloudOwner, cloudSiteId, tenantId, workloadId);
434 updateTimedOut = true;
438 sleep(updatePollInterval * 1000L);
440 pollTimeout -= updatePollInterval;
441 if (logger.isDebugEnabled())
442 logger.debug("pollTimeout remaining: " + pollTimeout);
446 } catch (MsoException me) {
447 if (logger.isDebugEnabled())
448 logger.debug("Multicloud AAI update exception: {} {} {} {}", cloudOwner, cloudSiteId, tenantId, workloadId, me);
452 if (updateTimedOut) {
453 if (logger.isDebugEnabled())
454 logger.debug("Multicloud AAI update request failed: {} {}", response.getStatus(), response.getStatusInfo().toString());
455 } else if (!HeatStatus.UPDATED.equals(stackInfo.getStatus())) {
456 if (logger.isDebugEnabled())
457 logger.debug("Multicloud AAI update request failed: {} {}", response.getStatus(), response.getStatusInfo().toString());
459 if (logger.isDebugEnabled())
460 logger.debug("Multicloud AAI update successful: {} {}", response.getStatus(), response.getStatusInfo().toString());
464 private StackInfo getStackStatus(String cloudSiteId, String cloudOwner, String tenantId, String instanceId) throws MsoException {
465 return getStackStatus(cloudSiteId, cloudOwner, tenantId, instanceId, false, 0, false);
468 private StackInfo getStackStatus(String cloudSiteId, String cloudOwner, String tenantId, String instanceId, boolean pollForCompletion, int timeoutMinutes, boolean backout) throws MsoException {
469 StackInfo stackInfo = new StackInfo();
471 // If client has requested a final response, poll for stack completion
472 if (pollForCompletion) {
473 // Set a time limit on overall polling.
474 // Use the resource (template) timeout for Openstack (expressed in minutes)
475 // and add one poll interval to give Openstack a chance to fail on its own.s
477 int createPollInterval = Integer.parseInt(this.environment.getProperty(createPollIntervalProp, createPollIntervalDefault));
478 int pollTimeout = (timeoutMinutes * 60) + createPollInterval;
479 // New 1610 - poll on delete if we rollback - use same values for now
480 int deletePollInterval = createPollInterval;
481 int deletePollTimeout = pollTimeout;
482 boolean createTimedOut = false;
483 StringBuilder stackErrorStatusReason = new StringBuilder("");
484 logger.debug("createPollInterval=" + createPollInterval + ", pollTimeout=" + pollTimeout);
488 stackInfo = queryStack(cloudSiteId, cloudOwner, tenantId, instanceId);
489 logger.debug (stackInfo.getStatus() + " (" + instanceId + ")");
491 if (HeatStatus.BUILDING.equals(stackInfo.getStatus())) {
492 // Stack creation is still running.
493 // Sleep and try again unless timeout has been reached
494 if (pollTimeout <= 0) {
495 // Note that this should not occur, since there is a timeout specified
496 // in the Openstack (multicloud?) call.
497 logger.error(String.format("%s %s %s %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT.toString(), cloudOwner, cloudSiteId, tenantId, instanceId, stackInfo.getStatus(), "", "", ErrorCode.AvailabilityError.getValue(), "Create stack timeout"));
498 createTimedOut = true;
502 sleep(createPollInterval * 1000L);
504 pollTimeout -= createPollInterval;
505 logger.debug("pollTimeout remaining: " + pollTimeout);
507 //save off the status & reason msg before we attempt delete
508 stackErrorStatusReason.append("Stack error (" + stackInfo.getStatus() + "): " + stackInfo.getStatusMessage());
511 } catch (MsoException me) {
512 // Cannot query the stack status. Something is wrong.
513 // Try to roll back the stack
515 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"));
518 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");
519 StackInfo deleteInfo = deleteStack(cloudSiteId, cloudOwner, tenantId, instanceId);
520 // this may be a waste of time - if we just got an exception trying to query the stack - we'll just
521 // get another one, n'est-ce pas?
522 boolean deleted = false;
525 StackInfo queryInfo = queryStack(cloudSiteId, cloudOwner, tenantId, instanceId);
526 logger.debug("Deleting " + instanceId + ", status: " + queryInfo.getStatus());
527 if (HeatStatus.DELETING.equals(queryInfo.getStatus())) {
528 if (deletePollTimeout <= 0) {
529 logger.error(String.format("%s %s %s %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT.toString(), cloudOwner, cloudSiteId, tenantId, instanceId,
530 queryInfo.getStatus(), "", "", ErrorCode.AvailabilityError.getValue(),
531 "Rollback: DELETE stack timeout"));
534 sleep(deletePollInterval * 1000L);
535 deletePollTimeout -= deletePollInterval;
537 } else if (HeatStatus.NOTFOUND.equals(queryInfo.getStatus())){
538 logger.debug("DELETE_COMPLETE for " + instanceId);
542 //got a status other than DELETE_IN_PROGRESS or DELETE_COMPLETE - so break and evaluate
545 } catch (Exception e3) {
546 // Just log this one. We will report the original exception.
547 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"));
550 } catch (Exception e2) {
551 // Just log this one. We will report the original exception.
552 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"));
556 // Propagate the original exception from Stack Query.
557 me.addContext (CREATE_STACK);
562 if (!HeatStatus.CREATED.equals(stackInfo.getStatus())) {
563 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: "
564 + stackInfo.getStatus () + ", " + stackInfo.getStatusMessage(), "", "", ErrorCode.BusinessProcesssError.getValue(), "Create Stack error"));
566 // Rollback the stack creation, since it is in an indeterminate state.
568 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"));
573 logger.debug("Create Stack errored - attempting to DELETE stack: " + instanceId);
574 logger.debug("deletePollInterval=" + deletePollInterval + ", deletePollTimeout=" + deletePollTimeout);
575 StackInfo deleteInfo = deleteStack(cloudSiteId, cloudOwner, tenantId, instanceId);
576 boolean deleted = false;
579 StackInfo queryInfo = queryStack(cloudSiteId, cloudOwner, tenantId, instanceId);
580 logger.debug("Deleting " + instanceId + ", status: " + queryInfo.getStatus());
581 if (HeatStatus.DELETING.equals(queryInfo.getStatus())) {
582 if (deletePollTimeout <= 0) {
583 logger.error(String.format("%s %s %s %s %s %s %s %s %d %s", MessageEnum.RA_CREATE_STACK_TIMEOUT.toString(), cloudOwner, cloudSiteId, tenantId, instanceId,
584 queryInfo.getStatus(), "", "", ErrorCode.AvailabilityError.getValue(),
585 "Rollback: DELETE stack timeout"));
588 sleep(deletePollInterval * 1000L);
589 deletePollTimeout -= deletePollInterval;
591 } else if (HeatStatus.NOTFOUND.equals(queryInfo.getStatus())){
592 logger.debug("DELETE_COMPLETE for " + instanceId);
596 //got a status other than DELETE_IN_PROGRESS or DELETE_COMPLETE - so break and evaluate
597 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"));
598 logger.debug("Stack deletion FAILED on a rollback of a create - " + instanceId + ", status=" + queryInfo.getStatus() + ", reason=" + queryInfo.getStatusMessage());
601 } catch (MsoException me2) {
602 // Just log this one. We will report the original exception.
603 logger.debug("Exception thrown trying to delete " + instanceId + " on a create->rollback: " + me2.getContextMessage(), me2);
604 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()));
607 StringBuilder errorContextMessage;
608 if (createTimedOut) {
609 errorContextMessage = new StringBuilder("Stack Creation Timeout");
611 errorContextMessage = stackErrorStatusReason;
614 errorContextMessage.append(" - stack successfully deleted");
616 errorContextMessage.append(" - encountered an error trying to delete the stack");
618 } catch (MsoException e2) {
619 // shouldn't happen - but handle
620 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"));
623 MsoOpenstackException me = new MsoOpenstackException(0, "", stackErrorStatusReason.toString());
624 me.addContext(CREATE_STACK);
628 // Get initial status, since it will have been null after the create.
629 stackInfo = queryStack(cloudSiteId, cloudOwner, tenantId, instanceId);
630 logger.debug("Multicloud stack query status is: " + stackInfo.getStatus());
635 private HeatStatus mapResponseToHeatStatus(Response response) {
636 if (response == null) {
637 return HeatStatus.FAILED;
638 } else if (response.getStatusInfo().getStatusCode() == Response.Status.OK.getStatusCode()) {
639 return HeatStatus.CREATED;
640 } else if (response.getStatusInfo().getStatusCode() == Response.Status.CREATED.getStatusCode()) {
641 return HeatStatus.CREATED;
642 } else if (response.getStatusInfo().getStatusCode() == Response.Status.NO_CONTENT.getStatusCode()) {
643 return HeatStatus.CREATED;
644 } else if (response.getStatusInfo().getStatusCode() == Response.Status.BAD_REQUEST.getStatusCode()) {
645 return HeatStatus.FAILED;
646 } else if (response.getStatusInfo().getStatusCode() == Response.Status.UNAUTHORIZED.getStatusCode()) {
647 return HeatStatus.FAILED;
648 } else if (response.getStatusInfo().getStatusCode() == Response.Status.NOT_FOUND.getStatusCode()) {
649 return HeatStatus.NOTFOUND;
650 } else if (response.getStatusInfo().getStatusCode() == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) {
651 return HeatStatus.FAILED;
653 return HeatStatus.UNKNOWN;
657 private MulticloudCreateResponse getCreateBody(java.io.InputStream in) {
658 Scanner scanner = new Scanner(in);
659 scanner.useDelimiter("\\Z");
661 if (scanner.hasNext()) {
662 body = scanner.next();
667 return new ObjectMapper().readerFor(MulticloudCreateResponse.class).readValue(body);
668 } catch (Exception e) {
669 logger.debug("Exception retrieving multicloud vfModule POST response body " + e);
674 private MulticloudQueryResponse getQueryBody(java.io.InputStream in) {
675 Scanner scanner = new Scanner(in);
676 scanner.useDelimiter("\\Z");
678 if (scanner.hasNext()) {
679 body = scanner.next();
684 return new ObjectMapper().readerFor(MulticloudQueryResponse.class).readValue(body);
685 } catch (Exception e) {
686 logger.debug("Exception retrieving multicloud workload query response body " + e);
691 private String getMulticloudEndpoint(String cloudSiteId, String cloudOwner, String workloadId) {
692 String msbIp = System.getenv().get(ONAP_IP);
693 if (null == msbIp || msbIp.isEmpty()) {
694 msbIp = environment.getProperty("mso.msb-ip", DEFAULT_MSB_IP);
696 Integer msbPort = environment.getProperty("mso.msb-port", Integer.class, DEFAULT_MSB_PORT);
698 String path = "/api/multicloud/v1/" + cloudOwner + "/" + cloudSiteId + "/infra_workload";
700 String endpoint = UriBuilder.fromPath(path).host(msbIp).port(msbPort).scheme("http").build().toString();
701 if (workloadId != null) {
702 if (logger.isDebugEnabled()) {
703 logger.debug(String.format("Multicloud Endpoint is: %s/%s", endpoint, workloadId));
705 return String.format("%s/%s", endpoint, workloadId);
707 if (logger.isDebugEnabled()) {
708 logger.debug(String.format("Multicloud Endpoint is: %s", endpoint));
714 private RestClient getMulticloudClient(String endpoint, String tenantId) {
715 HttpClient client = null;
717 client = httpClientFactory.newJsonClient(
719 TargetEntity.MULTICLOUD);
720 if (tenantId != null && !tenantId.isEmpty()) {
721 client.addAdditionalHeader("Project", tenantId);
723 } catch (MalformedURLException e) {
724 logger.debug(String.format("Encountered malformed URL error getting multicloud rest client %s", e.getMessage()));
725 } catch (IllegalArgumentException e) {
726 logger.debug(String.format("Encountered illegal argument getting multicloud rest client %s",e.getMessage()));
727 } catch (UriBuilderException e) {
728 logger.debug(String.format("Encountered URI builder error getting multicloud rest client %s", e.getMessage()));
733 private JsonNode getDirectiveNode(String directives) throws MsoException {
735 return JSON_MAPPER.readTree(directives);
736 } catch (Exception e) {
737 logger.error(String.format("%s %s %s %s %d %s",
738 MessageEnum.RA_CREATE_STACK_ERR.toString(),
739 "Create Stack: " + e, "", "",
740 ErrorCode.BusinessProcesssError.getValue(),
741 "Exception in Create Stack: Invalid JSON format of directives" + directives));
742 MsoException me = new MsoAdapterException("Invalid JSON format of directives parameter: " + directives);
743 me.addContext(CREATE_STACK);
749 * VduPlugin interface for instantiate function.
751 * Translate the VduPlugin parameters to the corresponding 'createStack' parameters,
752 * and then invoke the existing function.
755 public VduInstance instantiateVdu (
758 Map<String,Object> inputs,
759 VduModelInfo vduModel,
760 boolean rollbackOnFailure)
763 String cloudSiteId = cloudInfo.getCloudSiteId();
764 String cloudOwner = cloudInfo.getCloudOwner();
765 String tenantId = cloudInfo.getTenantId();
767 // Translate the VDU ModelInformation structure to that which is needed for
768 // creating the Heat stack. Loop through the artifacts, looking specifically
769 // for MAIN_TEMPLATE and ENVIRONMENT. Any other artifact will
770 // be attached as a FILE.
771 String heatTemplate = null;
772 Map<String,Object> nestedTemplates = new HashMap<>();
773 Map<String,Object> files = new HashMap<>();
774 String heatEnvironment = null;
776 for (VduArtifact vduArtifact: vduModel.getArtifacts()) {
777 if (vduArtifact.getType() == ArtifactType.MAIN_TEMPLATE) {
778 heatTemplate = new String(vduArtifact.getContent());
780 else if (vduArtifact.getType() == ArtifactType.NESTED_TEMPLATE) {
781 nestedTemplates.put(vduArtifact.getName(), new String(vduArtifact.getContent()));
783 else if (vduArtifact.getType() == ArtifactType.ENVIRONMENT) {
784 heatEnvironment = new String(vduArtifact.getContent());
789 StackInfo stackInfo = createStack (cloudSiteId,
796 true, // poll for completion
797 vduModel.getTimeoutMinutes(),
802 // Populate a vduInstance from the StackInfo
803 return stackInfoToVduInstance(stackInfo);
805 catch (Exception e) {
806 throw new VduException ("MsoMulticloudUtils (instantiateVDU): createStack Exception", e);
812 * VduPlugin interface for query function.
815 public VduInstance queryVdu (CloudInfo cloudInfo, String instanceId)
818 String cloudSiteId = cloudInfo.getCloudSiteId();
819 String cloudOwner = cloudInfo.getCloudOwner();
820 String tenantId = cloudInfo.getTenantId();
823 // Query the Cloudify Deployment object and populate a VduInstance
824 StackInfo stackInfo = queryStack (cloudSiteId, cloudOwner, tenantId, instanceId);
826 return stackInfoToVduInstance(stackInfo);
828 catch (Exception e) {
829 throw new VduException ("MsoMulticloudUtils (queryVdu): queryStack Exception ", e);
835 * VduPlugin interface for delete function.
838 public VduInstance deleteVdu (CloudInfo cloudInfo, String instanceId, int timeoutMinutes)
841 String cloudSiteId = cloudInfo.getCloudSiteId();
842 String cloudOwner = cloudInfo.getCloudOwner();
843 String tenantId = cloudInfo.getTenantId();
846 // Delete the Multicloud stack
847 StackInfo stackInfo = deleteStack (cloudSiteId, cloudOwner, tenantId, instanceId);
849 // Populate a VduInstance based on the deleted Cloudify Deployment object
850 VduInstance vduInstance = stackInfoToVduInstance(stackInfo);
852 // Override return state to DELETED (MulticloudUtils sets to NOTFOUND)
853 vduInstance.getStatus().setState(VduStateType.DELETED);
857 catch (Exception e) {
858 throw new VduException ("Delete VDU Exception", e);
864 * VduPlugin interface for update function.
866 * Update is currently not supported in the MsoMulticloudUtils implementation of VduPlugin.
867 * Just return a VduException.
871 public VduInstance updateVdu (
874 Map<String,Object> inputs,
875 VduModelInfo vduModel,
876 boolean rollbackOnFailure)
879 throw new VduException ("MsoMulticloudUtils: updateVdu interface not supported");
884 * Convert the local DeploymentInfo object (Cloudify-specific) to a generic VduInstance object
886 protected VduInstance stackInfoToVduInstance (StackInfo stackInfo)
888 VduInstance vduInstance = new VduInstance();
890 if (logger.isDebugEnabled()) {
891 logger.debug(String.format("StackInfo to convert: %s", stackInfo.getParameters().toString()));
893 // The full canonical name as the instance UUID
894 vduInstance.setVduInstanceId(stackInfo.getCanonicalName());
895 vduInstance.setVduInstanceName(stackInfo.getName());
897 // Copy inputs and outputs
898 vduInstance.setInputs(stackInfo.getParameters());
899 vduInstance.setOutputs(stackInfo.getOutputs());
901 // Translate the status elements
902 vduInstance.setStatus(stackStatusToVduStatus (stackInfo));
907 private VduStatus stackStatusToVduStatus (StackInfo stackInfo)
909 VduStatus vduStatus = new VduStatus();
911 // Map the status fields to more generic VduStatus.
912 // There are lots of HeatStatus values, so this is a bit long...
913 HeatStatus heatStatus = stackInfo.getStatus();
914 String statusMessage = stackInfo.getStatusMessage();
915 logger.debug("HeatStatus = " + heatStatus + " msg = " + statusMessage);
917 if (logger.isDebugEnabled()) {
918 logger.debug(String.format("Stack Status: %s", heatStatus.toString()));
919 logger.debug(String.format("Stack Status Message: %s", statusMessage));
922 if (heatStatus == HeatStatus.INIT || heatStatus == HeatStatus.BUILDING) {
923 vduStatus.setState(VduStateType.INSTANTIATING);
924 vduStatus.setLastAction((new PluginAction ("create", "in_progress", statusMessage)));
926 else if (heatStatus == HeatStatus.NOTFOUND) {
927 vduStatus.setState(VduStateType.NOTFOUND);
929 else if (heatStatus == HeatStatus.CREATED) {
930 vduStatus.setState(VduStateType.INSTANTIATED);
931 vduStatus.setLastAction((new PluginAction ("create", "complete", statusMessage)));
933 else if (heatStatus == HeatStatus.UPDATED) {
934 vduStatus.setState(VduStateType.INSTANTIATED);
935 vduStatus.setLastAction((new PluginAction ("update", "complete", statusMessage)));
937 else if (heatStatus == HeatStatus.UPDATING) {
938 vduStatus.setState(VduStateType.UPDATING);
939 vduStatus.setLastAction((new PluginAction ("update", "in_progress", statusMessage)));
941 else if (heatStatus == HeatStatus.DELETING) {
942 vduStatus.setState(VduStateType.DELETING);
943 vduStatus.setLastAction((new PluginAction ("delete", "in_progress", statusMessage)));
945 else if (heatStatus == HeatStatus.FAILED) {
946 vduStatus.setState(VduStateType.FAILED);
947 vduStatus.setErrorMessage(stackInfo.getStatusMessage());
949 vduStatus.setState(VduStateType.UNKNOWN);