diff --git a/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java b/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java index e6af2315ba2..f9952b2c03b 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java @@ -29,6 +29,7 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -36,7 +37,9 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.nio.TrustAllManager; +import com.google.gson.JsonElement; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; @@ -75,14 +78,15 @@ public class RedfishClient { private boolean ignoreSsl; private int redfishRequestMaxRetries; - private final static String SYSTEMS_URL_PATH = "redfish/v1/Systems/"; - private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "/Actions/ComputerSystem.Reset"; + private final static String SYSTEMS_URL_PATH = "redfish/v1/Systems"; + private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "Actions/ComputerSystem.Reset"; private final static String REDFISH_RESET_TYPE = "ResetType"; private final static String POWER_STATE = "PowerState"; private final static String APPLICATION_JSON = "application/json"; private final static String ACCEPT = "accept"; private final static String ODATA_ID = "@odata.id"; private final static String MEMBERS = "Members"; + private final static String LINKS = "Links"; private final static String EXPECTED_HTTP_STATUS = "2XX"; private final static int WAIT_FOR_REQUEST_RETRY = 2; @@ -265,12 +269,12 @@ public class RedfishClient { if (StringUtils.isBlank(resourceId)) { throw new RedfishException(String.format("Command '%s' requires a valid resource ID '%s'.", cmd, resourceId)); } - return String.format("%s%s", SYSTEMS_URL_PATH, resourceId); + return String.format("%s/%s", SYSTEMS_URL_PATH, resourceId); case ComputerSystemReset: if (StringUtils.isBlank(resourceId)) { throw new RedfishException(String.format("Command '%s' requires a valid resource ID '%s'.", cmd, resourceId)); } - return String.format("%s%s%s", SYSTEMS_URL_PATH, resourceId, COMPUTER_SYSTEM_RESET_URL_PATH); + return String.format("%s/%s/%s", SYSTEMS_URL_PATH, resourceId, COMPUTER_SYSTEM_RESET_URL_PATH); default: throw new RedfishException(String.format("Redfish client does not support command '%s'.", cmd)); } @@ -305,8 +309,8 @@ public class RedfishClient { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) { - throw new RedfishException(String.format("Failed to get System power state for host '%s' with request '%s: %s'. The expected HTTP status code is '%s' but it got '%s'.", - HttpGet.METHOD_NAME, url, hostAddress, EXPECTED_HTTP_STATUS, statusCode)); + throw new RedfishException(String.format("Failed to execute System power command for host by performing '%s' request on URL '%s' and host address '%s'. The expected HTTP status code is '%s' but it got '%s'.", + HttpPost.METHOD_NAME, url, hostAddress, EXPECTED_HTTP_STATUS, statusCode)); } logger.debug(String.format("Sending ComputerSystem.Reset Command '%s' to host '%s' with request '%s %s'", resetCommand, hostAddress, HttpPost.METHOD_NAME, url)); } @@ -340,16 +344,25 @@ public class RedfishClient { try { in = response.getEntity().getContent(); BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - jsonString = streamReader.readLine(); + jsonString = streamReader.lines().collect(Collectors.joining()); } catch (UnsupportedOperationException | IOException e) { throw new RedfishException("Failed to process system Response", e); } // retrieving the system ID (e.g. 'System.Embedded.1') via JsonParser: // (...) Members":[{"@odata.id":"/redfish/v1/Systems/System.Embedded.1"}] (...) - JsonArray jArray = new JsonParser().parse(jsonString).getAsJsonObject().get(MEMBERS).getAsJsonArray(); - JsonObject jsonnObject = jArray.get(0).getAsJsonObject(); - String jsonObjectAsString = jsonnObject.get(ODATA_ID).getAsString(); + JsonArray jArray = null; + JsonElement jsonElement = new JsonParser().parse(jsonString); + if (jsonElement.getAsJsonObject().get(MEMBERS) != null) { + jArray = jsonElement.getAsJsonObject().get(MEMBERS).getAsJsonArray(); + } else if (jsonElement.getAsJsonObject().get(LINKS) != null){ + jArray = jsonElement.getAsJsonObject().get(LINKS).getAsJsonObject().get(MEMBERS).getAsJsonArray(); + } + if (jArray == null || jArray.size() < 1) { + throw new CloudRuntimeException("Members not found in the Redfish Systems JSON, unable to determine Redfish system ID"); + } + JsonObject jsonObject = jArray.get(0).getAsJsonObject(); + String jsonObjectAsString = jsonObject.get(ODATA_ID).getAsString(); String[] arrayOfStrings = StringUtils.split(jsonObjectAsString, '/'); return arrayOfStrings[arrayOfStrings.length - 1]; @@ -384,8 +397,7 @@ public class RedfishClient { try { in = response.getEntity().getContent(); BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - - jsonString = streamReader.readLine(); + jsonString = streamReader.lines().collect(Collectors.joining()); String powerState = new JsonParser().parse(jsonString).getAsJsonObject().get(POWER_STATE).getAsString(); return RedfishPowerState.valueOf(powerState); } catch (UnsupportedOperationException | IOException e) { diff --git a/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java b/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java index 58dfbceb66a..83098cabf38 100644 --- a/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java +++ b/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java @@ -41,9 +41,9 @@ public class RedfishClientTest { private static final String PASSWORD = "password"; private static final String oobAddress = "oob.host.address"; private static final String systemId = "SystemID.1"; - private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "/Actions/ComputerSystem.Reset"; + private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "Actions/ComputerSystem.Reset"; private final static Integer REDFISHT_REQUEST_RETRIES = Integer.valueOf(2); - private static final String url = "https://address.system.net/redfish/v1/Systems/"; + private static final String url = "https://address.system.net/redfish/v1/Systems"; private static final HttpRequestBase httpReq = new HttpGet(url); @Mock @@ -87,7 +87,7 @@ public class RedfishClientTest { public void buildRequestUrlTestHttpsGetSystemId() { RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, true, false, REDFISHT_REQUEST_RETRIES); String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.GetSystemId, systemId); - String expected = String.format("https://%s/redfish/v1/Systems/", oobAddress); + String expected = String.format("https://%s/redfish/v1/Systems", oobAddress); Assert.assertEquals(expected, result); } @@ -95,7 +95,7 @@ public class RedfishClientTest { public void buildRequestUrlTestGetSystemId() { RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, false, false, REDFISHT_REQUEST_RETRIES); String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.GetSystemId, systemId); - String expected = String.format("http://%s/redfish/v1/Systems/", oobAddress); + String expected = String.format("http://%s/redfish/v1/Systems", oobAddress); Assert.assertEquals(expected, result); } @@ -103,7 +103,7 @@ public class RedfishClientTest { public void buildRequestUrlTestHttpsComputerSystemReset() { RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, true, false, REDFISHT_REQUEST_RETRIES); String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.ComputerSystemReset, systemId); - String expected = String.format("https://%s/redfish/v1/Systems/%s%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH); + String expected = String.format("https://%s/redfish/v1/Systems/%s/%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH); Assert.assertEquals(expected, result); } @@ -111,7 +111,7 @@ public class RedfishClientTest { public void buildRequestUrlTestComputerSystemReset() { RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, false, false, REDFISHT_REQUEST_RETRIES); String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.ComputerSystemReset, systemId); - String expected = String.format("http://%s/redfish/v1/Systems/%s%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH); + String expected = String.format("http://%s/redfish/v1/Systems/%s/%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH); Assert.assertEquals(expected, result); }