diff options
author | Steve Smokowski <ss835w@att.com> | 2019-04-11 11:41:29 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2019-04-11 11:41:29 +0000 |
commit | fb4b7ea522f859eeb8813f30004dd7c1fb7b3409 (patch) | |
tree | b46bba37c760982a3bd12a65be9372998fc61b5c /adapters/mso-adapter-utils | |
parent | f4c77048e9fa8e708ff9fb5c09448623acc3ef4c (diff) | |
parent | 287362502adc4485fc43f9364fd1829adec55ab6 (diff) |
Merge "Update multicloud plugin GET handling"
Diffstat (limited to 'adapters/mso-adapter-utils')
7 files changed, 206 insertions, 33 deletions
diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java index 2788387bc1..7bf68fff78 100644 --- a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java +++ b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoMulticloudUtils.java @@ -25,6 +25,7 @@ package org.onap.so.openstack.utils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.woorea.openstack.heat.model.CreateStackParam; +import com.woorea.openstack.heat.model.Stack; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; @@ -56,6 +57,7 @@ import org.onap.so.openstack.exceptions.MsoAdapterException; import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound; import org.onap.so.openstack.exceptions.MsoException; import org.onap.so.openstack.exceptions.MsoOpenstackException; +import org.onap.so.openstack.mappers.StackInfoMapper; import org.onap.so.utils.TargetEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -195,7 +197,7 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { logger.debug(String.format("Multicloud Request is: %s", multicloudRequest.toString())); } - String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, null); + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, null, false); RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId); if (multicloudClient == null) { @@ -222,8 +224,7 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { timeoutMinutes, backout); if (HeatStatus.CREATED.equals(stackStatus.getStatus())) { - String workloadId = multicloudResponseBody == null ? null : multicloudResponseBody.getWorkloadId(); - multicloudAaiUpdate(cloudSiteId, cloudOwner, tenantId, genericVnfId, vfModuleId, workloadId, + multicloudAaiUpdate(cloudSiteId, cloudOwner, tenantId, genericVnfId, vfModuleId, canonicalName, pollForCompletion, timeoutMinutes); } @@ -268,6 +269,7 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { } String stackName = null; String stackId = null; + boolean byName = false; int offset = instanceId.indexOf('/'); if (offset > 0 && offset < (instanceId.length() - 1)) { stackName = instanceId.substring(0, offset); @@ -275,12 +277,13 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { } else { stackName = instanceId; stackId = instanceId; + byName = true; } StackInfo returnInfo = new StackInfo(); returnInfo.setName(stackName); - String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId); + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId, byName); RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId); if (multicloudClient != null) { @@ -289,24 +292,36 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { logger.debug(String.format("Multicloud GET Response: %s", response.toString())); } - MulticloudQueryResponse multicloudQueryBody = null; + MulticloudQueryResponse responseBody = null; if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { returnInfo.setStatus(HeatStatus.NOTFOUND); returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase()); } else if (response.getStatus() == Response.Status.OK.getStatusCode() && response.hasEntity()) { - multicloudQueryBody = getQueryBody((java.io.InputStream) response.getEntity()); - if (multicloudQueryBody != null) { - returnInfo.setCanonicalName(stackName + "/" + multicloudQueryBody.getWorkloadId()); - returnInfo.setStatus(getHeatStatus(multicloudQueryBody.getWorkloadStatus())); - returnInfo.setStatusMessage(multicloudQueryBody.getWorkloadStatus()); + responseBody = getQueryBody((java.io.InputStream) response.getEntity()); + if (responseBody != null) { if (logger.isDebugEnabled()) { - logger.debug("Multicloud Create Response Body: " + multicloudQueryBody.toString()); + logger.debug("Multicloud Create Response Body: " + responseBody.toString()); + } + Stack workloadStack = getWorkloadStack(responseBody.getWorkloadStatusReason()); + if (workloadStack != null && !responseBody.getWorkloadStatus().equals("GET_FAILED") + && !responseBody.getWorkloadStatus().contains("UPDATE")) { + returnInfo = new StackInfoMapper(workloadStack).map(); + } else { + returnInfo.setCanonicalName(stackName + "/" + responseBody.getWorkloadId()); + returnInfo.setStatus(getHeatStatus(responseBody.getWorkloadStatus())); + returnInfo.setStatusMessage(responseBody.getWorkloadStatus()); } } else { + returnInfo.setName(stackName); + if (!byName) + returnInfo.setCanonicalName(instanceId); returnInfo.setStatus(HeatStatus.FAILED); returnInfo.setStatusMessage(MULTICLOUD_QUERY_BODY_NULL); } } else { + returnInfo.setName(stackName); + if (!byName) + returnInfo.setCanonicalName(instanceId); returnInfo.setStatus(HeatStatus.FAILED); returnInfo.setStatusMessage(response.getStatusInfo().getReasonPhrase()); } @@ -315,6 +330,35 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { return returnInfo; } + private Stack getWorkloadStack(JsonNode node) { + if (node == null) + return null; + Stack workloadStack = null; + if (node.has("stacks")) { + try { + if (!node.at("/stacks/0").isNull() && node.at("/stacks/0").has("stack_status")) { + workloadStack = JSON_MAPPER.treeToValue(node.at("/stacks/0"), Stack.class); + } else { + workloadStack = new Stack(); + workloadStack.setStackStatus(HeatStatus.NOTFOUND.toString()); + } + } catch (Exception e) { + logger.debug("Multicloud Get Exception mapping /stack/0: {} ", node.toString(), e); + } + } else if (node.has("stack")) { + try { + if (node.at("/stack").has("stack_status")) { + workloadStack = JSON_MAPPER.treeToValue(node.at("/stack"), Stack.class); + } + } catch (Exception e) { + logger.debug("Multicloud Get Exception mapping /stack: {} ", node.toString(), e); + } + } + if (workloadStack != null) + logger.debug("Multicloud getWorkloadStack() returning Stack Object: {} ", workloadStack.toString()); + return workloadStack; + } + public StackInfo deleteStack(String cloudSiteId, String cloudOwner, String tenantId, String instanceId) throws MsoException { if (logger.isDebugEnabled()) { @@ -335,7 +379,7 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { returnInfo.setName(stackName); Response response = null; - String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId); + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId, false); RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId); if (multicloudClient != null) { @@ -389,12 +433,20 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { private void multicloudAaiUpdate(String cloudSiteId, String cloudOwner, String tenantId, String genericVnfId, String vfModuleId, String workloadId, boolean pollForCompletion, int timeoutMinutes) { + String stackId = null; + int offset = workloadId.indexOf('/'); + if (offset > 0 && offset < (workloadId.length() - 1)) { + stackId = workloadId.substring(offset + 1); + } else { + stackId = workloadId; + } + MulticloudRequest multicloudRequest = new MulticloudRequest(); multicloudRequest.setGenericVnfId(genericVnfId); multicloudRequest.setVfModuleId(vfModuleId); - String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, workloadId); + String multicloudEndpoint = getMulticloudEndpoint(cloudSiteId, cloudOwner, stackId, false); RestClient multicloudClient = getMulticloudClient(multicloudEndpoint, tenantId); if (multicloudClient == null) { @@ -740,7 +792,7 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { return null; } - private String getMulticloudEndpoint(String cloudSiteId, String cloudOwner, String workloadId) { + private String getMulticloudEndpoint(String cloudSiteId, String cloudOwner, String workloadId, boolean isName) { String msbIp = System.getenv().get(ONAP_IP); if (null == msbIp || msbIp.isEmpty()) { msbIp = environment.getProperty("mso.msb-ip", DEFAULT_MSB_IP); @@ -751,10 +803,16 @@ public class MsoMulticloudUtils extends MsoHeatUtils implements VduPlugin { String endpoint = UriBuilder.fromPath(path).host(msbIp).port(msbPort).scheme("http").build().toString(); if (workloadId != null) { + String middlepart = null; + if (isName) { + middlepart = "?name="; + } else { + middlepart = "/"; + } if (logger.isDebugEnabled()) { - logger.debug(String.format("Multicloud Endpoint is: %s/%s", endpoint, workloadId)); + logger.debug(String.format("Multicloud Endpoint is: %s%s%s", endpoint, middlepart, workloadId)); } - return String.format("%s/%s", endpoint, workloadId); + return String.format("%s%s%s", endpoint, middlepart, workloadId); } else { if (logger.isDebugEnabled()) { logger.debug(String.format("Multicloud Endpoint is: %s", endpoint)); diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MulticloudQueryResponse.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MulticloudQueryResponse.java index ca8f041aae..a22937aea3 100644 --- a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MulticloudQueryResponse.java +++ b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MulticloudQueryResponse.java @@ -25,10 +25,11 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.lang.builder.ToStringBuilder; @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({"template_type", "workload_id", "workload_status"}) +@JsonPropertyOrder({"template_type", "workload_id", "workload_status", "workload_status_reason"}) public class MulticloudQueryResponse implements Serializable { private final static long serialVersionUID = -5215028275577848311L; @@ -38,13 +39,17 @@ public class MulticloudQueryResponse implements Serializable { private String workloadId; @JsonProperty("workload_status") private String workloadStatus; + @JsonProperty("workload_status_reason") + private JsonNode workloadStatusReason; @JsonCreator public MulticloudQueryResponse(@JsonProperty("template_type") String templateType, - @JsonProperty("workload_id") String workloadId, @JsonProperty("workload_status") String workloadStatus) { + @JsonProperty("workload_id") String workloadId, @JsonProperty("workload_status") String workloadStatus, + @JsonProperty("workload_status_reason") JsonNode workloadStatusReason) { this.templateType = templateType; this.workloadId = workloadId; this.workloadStatus = workloadStatus; + this.workloadStatusReason = workloadStatusReason; } @JsonProperty("template_type") @@ -77,9 +82,20 @@ public class MulticloudQueryResponse implements Serializable { this.workloadStatus = workloadStatus; } + @JsonProperty("workload_status_reason") + public JsonNode getWorkloadStatusReason() { + return workloadStatusReason; + } + + @JsonProperty("workload_status_reason") + public void setWorkloadStatus(JsonNode workloadStatusReason) { + this.workloadStatusReason = workloadStatusReason; + } + @Override public String toString() { return new ToStringBuilder(this).append("templateType", templateType).append("workloadId", workloadId) - .append("workloadStatus", workloadStatus).toString(); + .append("workloadStatus", workloadStatus).append("workloadStatusReason", workloadStatusReason) + .toString(); } } diff --git a/adapters/mso-adapter-utils/src/test/java/org/onap/so/openstack/utils/MsoMulticloudUtilsTest.java b/adapters/mso-adapter-utils/src/test/java/org/onap/so/openstack/utils/MsoMulticloudUtilsTest.java index 0d8f451a6d..995db522e9 100644 --- a/adapters/mso-adapter-utils/src/test/java/org/onap/so/openstack/utils/MsoMulticloudUtilsTest.java +++ b/adapters/mso-adapter-utils/src/test/java/org/onap/so/openstack/utils/MsoMulticloudUtilsTest.java @@ -21,9 +21,11 @@ package org.onap.so.openstack.utils; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -34,7 +36,6 @@ import java.io.IOException; import java.util.HashMap; import java.util.Optional; import org.apache.http.HttpStatus; -import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.mockito.InjectMocks; @@ -64,38 +65,36 @@ public class MsoMulticloudUtilsTest extends BaseTest { @Mock private CloudConfig cloudConfigMock; - private static final String CREATE_STACK_RESPONSE = "{\"template_type\": \"TEST-template\", \"workload_id\": " + private static final String CREATE_STACK_RESPONSE = "{\"template_type\": \"heat\", \"workload_id\": " + "\"TEST-workload\", \"template_response\": {\"stack\": {\"id\": \"TEST-stack\", \"links\": []}}}"; private static final String UPDATE_STACK_RESPONSE = - "{\"template_type\": \"TEST-template\", \"workload_id\": " + "\"TEST-workload\"}"; - private static final String GET_CREATE_STACK_RESPONSE = "{\"template_type\": \"TEST-template\", \"workload_id\": " - + "\"TEST-workload\", \"workload_status\": \"CREATE_COMPLETE\"}"; - private static final String GET_UPDATE_STACK_RESPONSE = "{\"template_type\": \"TEST-template\", \"workload_id\": " - + "\"TEST-workload\", \"workload_status\": \"UPDATE_COMPLETE\"}"; + "{\"template_type\": \"heat\", \"workload_id\": " + "\"TEST-workload\"}"; private static final String MULTICLOUD_CREATE_PATH = "/api/multicloud/v1/CloudOwner/MTN14/infra_workload"; private static final String MULTICLOUD_UPDATE_PATH = "/api/multicloud/v1/CloudOwner/MTN14/infra_workload/TEST-workload"; private static final String MULTICLOUD_GET_PATH = "/api/multicloud/v1/CloudOwner/MTN14/infra_workload/TEST-workload"; + private static final String MULTICLOUD_DELETE_PATH = + "/api/multicloud/v1/CloudOwner/MTN14/infra_workload/TEST-workload"; @Test public void createStackSuccess() throws MsoException, IOException { wireMockServer - .stubFor(post(urlPathEqualTo(MULTICLOUD_CREATE_PATH)).inScenario("CREATE") + .stubFor(post(urlEqualTo(MULTICLOUD_CREATE_PATH)).inScenario("CREATE") .willReturn(aResponse().withHeader("Content-Type", "application/json") .withBody(CREATE_STACK_RESPONSE).withStatus(HttpStatus.SC_CREATED)) .willSetStateTo("CREATING")); wireMockServer.stubFor(get(urlPathEqualTo(MULTICLOUD_GET_PATH)).inScenario("CREATE") .whenScenarioStateIs("CREATING").willReturn(aResponse().withHeader("Content-Type", "application/json") - .withBody(GET_CREATE_STACK_RESPONSE).withStatus(HttpStatus.SC_OK))); + .withBodyFile("MulticloudGetCreateResponse.json").withStatus(HttpStatus.SC_OK))); wireMockServer.stubFor(post(urlPathEqualTo(MULTICLOUD_UPDATE_PATH)) .inScenario("CREATE").willReturn(aResponse().withHeader("Content-Type", "application/json") .withBody(UPDATE_STACK_RESPONSE).withStatus(HttpStatus.SC_ACCEPTED)) .willSetStateTo("UPDATING")); - wireMockServer.stubFor(get(urlPathEqualTo(MULTICLOUD_GET_PATH)).inScenario("CREATE") - .whenScenarioStateIs("UPDATING").willReturn(aResponse().withHeader("Content-Type", "application/json") - .withBody(GET_UPDATE_STACK_RESPONSE).withStatus(HttpStatus.SC_OK))); + wireMockServer.stubFor(get(urlEqualTo(MULTICLOUD_GET_PATH)).inScenario("CREATE").whenScenarioStateIs("UPDATING") + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withBodyFile("MulticloudGetUpdateResponse.json").withStatus(HttpStatus.SC_OK))); StackInfo result = multicloudUtils.createStack("MTN14", "CloudOwner", "TEST-tenant", "TEST-stack", new VduModelInfo(), "TEST-heat", new HashMap<>(), true, 200, "TEST-env", new HashMap<>(), new HashMap<>(), false); @@ -106,7 +105,13 @@ public class MsoMulticloudUtilsTest extends BaseTest { @Test public void deleteStack() throws MsoException { - StackInfo result = multicloudUtils.deleteStack("MTN13", "CloudOwner", "TEST-tenant", "instanceId"); + wireMockServer.stubFor(delete(urlEqualTo(MULTICLOUD_DELETE_PATH)).willReturn( + aResponse().withHeader("Content-Type", "application/json").withStatus(HttpStatus.SC_NO_CONTENT))); + wireMockServer.stubFor(get(urlEqualTo(MULTICLOUD_GET_PATH)) + .willReturn(aResponse().withHeader("Content-Type", "application/json") + .withBodyFile("MulticloudGetDeleteResponse.json").withStatus(HttpStatus.SC_OK))); + StackInfo result = + multicloudUtils.deleteStack("MTN14", "CloudOwner", "TEST-tenant", "TEST-stack/TEST-workload"); assertNotNull(result); assertTrue(HeatStatus.NOTFOUND == result.getStatus()); } @@ -123,7 +128,7 @@ public class MsoMulticloudUtilsTest extends BaseTest { .willReturn(aResponse().withHeader("Content-Type", "application/json").withBody(CREATE_STACK_RESPONSE) .withStatus(HttpStatus.SC_OK))); - StackInfo result = multicloudUtils.queryStack("MTN13", "CloudOwner", "TEST-tenant", "instanceId"); + StackInfo result = multicloudUtils.queryStack("MTN13", "CloudOwner", "TEST-tenant", "instanceName/instanceId"); assertTrue(HeatStatus.FAILED == result.getStatus()); assertEquals(MULTICLOUD_QUERY_BODY_NULL, result.getStatusMessage()); } diff --git a/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetByNameResponse.json b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetByNameResponse.json new file mode 100644 index 0000000000..7e671d005a --- /dev/null +++ b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetByNameResponse.json @@ -0,0 +1,34 @@ +{ + "template_type": "heat", + "workload_id": "TEST-workload", + "workload_status": "GET_COMPLETE", + "workload_status_reason": { + "stacks": [ + { + "id": "TEST-workload", + "links" : [ + { + "href" : "port", + "rel" : "self" + } + ], + "stack_name": "TEST-stack", + "stack_status": "CREATE_COMPLETE", + "creation_time": "2019-03-21T03:17:32Z", + "description": "Sample stack response", + "stack_owner": "stack_Owner", + "capabilities": [], + "parameters": { + "OS::project_id": "0e148b76ee8c42f78d37013bf6b7b1ae", + "apt_proxy": "10.12.5.2:8000", + "etcd_vm_flavor": "m1.medium", + "docker_version": "17.03.2", + "portal_hostname": "portal.api.simpledemo.onap.org", + "docker_proxy": "10.12.5.2:5000", + "kubectl_version": "1.11.5", + "use_ramdisk": "false" + } + } + ] + } +} diff --git a/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetCreateResponse.json b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetCreateResponse.json new file mode 100644 index 0000000000..8feb3e085e --- /dev/null +++ b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetCreateResponse.json @@ -0,0 +1,44 @@ +{ + "template_type": "heat", + "workload_id": "TEST-workload", + "workload_status": "CREATE_COMPLETE", + "workload_status_reason": { + "stack": { + "id": "TEST-workload", + "links" : [ + { + "href" : "port", + "rel" : "self" + } + ], + "stack_name": "TEST-stack", + "stack_status": "CREATE_COMPLETE", + "creation_time": "2019-03-21T03:17:32Z", + "description": "Sample stack response", + "stack_owner": "stack_Owner", + "capabilities": [], + "parameters": { + "OS::project_id": "0e148b76ee8c42f78d37013bf6b7b1ae", + "apt_proxy": "10.12.5.2:8000", + "etcd_vm_flavor": "m1.medium", + "docker_version": "17.03.2", + "portal_hostname": "portal.api.simpledemo.onap.org", + "docker_proxy": "10.12.5.2:5000", + "kubectl_version": "1.11.5", + "use_ramdisk": "false" + }, + "outputs": [ + { + "output_value": "10.12.6.20", + "output_key": "k8s_12_vm_ip", + "description": "The IP address of the k8s_12 instance" + }, + { + "output_value": "10.12.5.241", + "output_key": "k8s_01_vm_ip", + "description": "The IP address of the k8s_01 instance" + } + ] + } + } +} diff --git a/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetDeleteResponse.json b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetDeleteResponse.json new file mode 100644 index 0000000000..9efaf7b265 --- /dev/null +++ b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetDeleteResponse.json @@ -0,0 +1,8 @@ +{ + "template_type": "heat", + "workload_id": "TEST-workload", + "workload_status": "DELETE_COMPLETE", + "workload_status_reason": { + "stack": {} + } +} diff --git a/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetUpdateResponse.json b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetUpdateResponse.json new file mode 100644 index 0000000000..84d7db82ba --- /dev/null +++ b/adapters/mso-adapter-utils/src/test/resources/__files/MulticloudGetUpdateResponse.json @@ -0,0 +1,8 @@ +{ + "template_type": "heat", + "workload_id": "TEST-workload", + "workload_status": "UPDATE_COMPLETE", + "workload_status_reason": { + "stack": {} + } +} |