You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by GitBox <gi...@apache.org> on 2020/06/24 13:58:04 UTC

[GitHub] [cloudstack] GabrielBrascher opened a new pull request #4175: Redfish Client & Redfish OOBM Driver

GabrielBrascher opened a new pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175


   ## Description
   <!--- Describe your changes in detail -->
   
   This PR adds support for the OOBM Redfish protocol, implementing a Java client to send HTTP requests to Redfish supported systems.
   
   Implementation overview:
   - Redfish Java client: a Java Client for Redfish that makes Redfish actions available to the HA workflow via an OOB driver.
   - OOB Redfish driver: a new Out-of-band driver was created for Redfish, allowing to integrate the Redfish Client with the CloudStack Out-of-band management implementation.
   
   <!-- For new features, provide link to FS, dev ML discussion etc. -->
   <!-- In case of bug fix, the expected and actual behaviours, steps to reproduce. -->
   
   Fixes: 3624
   
   **Thanks note:** this implementation counted with the help from our friends at [Yourhosting](https://www.yourhosting.nl/).
   
   ## Types of changes
   <!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
   - [ ] Breaking change (fix or feature that would cause existing functionality to change)
   - [ ] New feature (non-breaking change which adds functionality)
   - [ ] Bug fix (non-breaking change which fixes an issue)
   - [ ] Enhancement (improves an existing feature and functionality)
   - [ ] Cleanup (Code refactoring and cleanup, that may add test cases)
   
   ## Screenshots (if appropriate):
   Configure OOBM with Redfish driver
   ![image](https://user-images.githubusercontent.com/5025148/85565017-1fa1a380-b605-11ea-97d0-786d9eb976da.png)
   Check OOBM host tab
   ![image](https://user-images.githubusercontent.com/5025148/85565051-26c8b180-b605-11ea-9a5c-aa393ec44fa3.png)
   Test powering On and Off hosts. Host status is properly shown
   ![image](https://user-images.githubusercontent.com/5025148/85567065-eff39b00-b606-11ea-94b2-7809aa5504be.png)
   
   ## How Has This Been Tested?
   <!-- Please describe in detail how you tested your changes. -->
   <!-- Include details of your testing environment, and the tests you ran to -->
   <!-- see how your change affects other areas of the code, etc. -->
   We have been able to test on Dell and Supermicro machines and it works fine:
   
   1. via the CloudStack UI, enable redfish oob driver for host n05
   2. verified that ACS can retrieve the redfish system ID and system power state
   3. powering Off node works as expected, host state transition worked well and the host was indeed powered off
   4. powering On also worked as expected, CloudStack host state transition as expected, host is indeed up again
   
   
   <!-- Please read the [CONTRIBUTING](https://github.com/apache/cloudstack/blob/master/CONTRIBUTING.md) document -->
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] wido commented on a change in pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
wido commented on a change in pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#discussion_r445082792



##########
File path: utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java
##########
@@ -0,0 +1,378 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package org.apache.cloudstack.utils.redfish;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+
+import com.cloud.utils.nio.TrustAllManager;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.protocol.HTTP;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.net.NetUtils;
+import com.google.common.net.InternetDomainName;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Provides support to a set of REST requests that can be sent to a Redfish Server. </br>
+ * RedfishClient allows to gather the server Power State, and execute Reset
+ * actions such as 'On', 'ForceOff', 'GracefulShutdown', 'GracefulRestart' etc.
+ */
+public class RedfishClient {
+
+    private static final Logger LOGGER = Logger.getLogger(RedfishClient.class);
+
+    private String username;
+    private String password;
+    private boolean useHttps;
+    private boolean ignoreSsl;
+
+    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 EXPECTED_HTTP_STATUS = "2XX";
+
+    /**
+     * Redfish Command type: </br>
+     * <b>ComputerSystemReset:</b> execute Redfish reset commands ({@link RedfishResetCmd}). </br>
+     * <b>GetSystemId:</b> get the system ID. </br>
+     * <b>GetPowerState:</b> used for get the system power state. </br>
+     */
+    public enum
+    RedfishCmdType {
+        ComputerSystemReset, GetSystemId, GetPowerState
+    }
+
+    /**
+     * Redfish System Power State: </br>
+     * <b>Off:</b> The state is powered Off. </br>
+     * <b>On:</b> The state is powered On. </br>
+     * <b>PoweringOff:</b> A temporary state between On and Off. </br>
+     * <b>PoweringOn:</b> A temporary state between Off and On.
+     */
+    public enum RedfishPowerState {
+        On, Off, PoweringOn, PoweringOff
+    }
+
+    /**
+     * <ul>
+     * <li><b>ForceOff:</b> Turn the unit off immediately (non-graceful shutdown).
+     * <li><b>ForceOn:</b> Turn the unit on immediately.
+     * <li><b>ForceRestart:</b> Perform an immediate (non-graceful) shutdown,
+     * followed by a restart.
+     * <li><b>GracefulRestart:</b> Perform a graceful shutdown followed by a restart
+     * of the system.
+     * <li><b>GracefulShutdown:</b> Perform a graceful shutdown and power off.
+     * <li><b>Nmi:</b> Generate a Diagnostic Interrupt (usually an NMI on x86
+     * systems) to cease normal operations, perform diagnostic actions and typically
+     * halt the system.
+     * <li><b>On:</b> Turn the unit on.
+     * <li><b>PowerCycle:</b> Perform a power cycle of the unit.
+     * <li><b>PushPowerButton:</b> Simulate the pressing of the physical power
+     * button on this unit.
+     * </ul>
+     */
+    public enum RedfishResetCmd {
+        ForceOff, ForceOn, ForceRestart, GracefulRestart, GracefulShutdown, Nmi, On, PowerCycle, PushPowerButton
+    }
+
+    public RedfishClient(String username, String password, boolean useHttps, boolean ignoreSsl) {
+        this.username = username;
+        this.password = password;
+        this.useHttps = useHttps;
+        this.ignoreSsl = ignoreSsl;
+    }
+
+    protected String buildRequestUrl(String hostAddress, RedfishCmdType cmd, String resourceId) {
+        String urlHostAddress = validateAddressAndPrepareForUrl(hostAddress);
+        String requestPath = getRequestPathForCommand(cmd, resourceId);
+
+        if (useHttps) {
+            return String.format("https://%s/%s", urlHostAddress, requestPath);
+        } else {
+            return String.format("http://%s/%s", urlHostAddress, requestPath);
+        }
+    }
+
+    /**
+     * Executes a GET request for the given URL address.
+     */
+    protected HttpResponse executeGetRequest(String url) {
+        URIBuilder builder = null;
+        HttpGet httpReq = null;
+
+        try {
+            builder = new URIBuilder(url);
+            httpReq = new HttpGet(builder.build());;
+            httpReq.addHeader(ACCEPT, APPLICATION_JSON);
+            String encoding = basicAuth(username, password);
+            httpReq.addHeader("Authorization", encoding);
+        } catch (URISyntaxException e) {
+            throw new RedfishException(String.format("Failed to create URI for GET request [URL: %s] due to exception.", url), e);
+        }
+
+        HttpClient client = null;
+        if (ignoreSsl) {
+            try {
+                client = ignoreSSLCertValidator();
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                throw new RedfishException(String.format("Failed to handle SSL Cert validator on GET request [URL: %s] due to exception.", url), e);
+            }
+        } else {
+            client = HttpClientBuilder.create().build();
+        }
+        try {
+            return client.execute(httpReq);
+        } catch (IOException e) {
+            throw new RedfishException(String.format("Failed to execute GET request [URL: %s] due to exception.", url), e);
+        }
+    }
+
+    private static String basicAuth(String username, String password) {
+        return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
+    }
+
+    /**
+     * Executes a POST request for the given URL address and Json object.
+     */
+    private HttpResponse executePostRequest(String url, JsonObject jsonToSend) {
+        HttpPost httpReq = null;
+        try {
+            URIBuilder builder = new URIBuilder(url);
+            httpReq = new HttpPost(builder.build());
+            httpReq.addHeader(ACCEPT, APPLICATION_JSON);
+            httpReq.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
+            String encoding = basicAuth(username, password);
+            httpReq.addHeader("Authorization", encoding);
+            httpReq.setEntity(new StringEntity(jsonToSend.toString()));
+        } catch (URISyntaxException | UnsupportedEncodingException e) {
+            throw new RedfishException(String.format("Failed to create URI for POST request [URL: %s] due to exception.", url), e);
+        }
+
+        HttpClient client = null;
+        if (ignoreSsl) {
+            try {
+                client = ignoreSSLCertValidator();
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                throw new RedfishException(String.format("Failed to handle SSL Cert validator on POST request [URL: %s] due to exception.", url), e);
+            }
+        } else {
+            client = HttpClientBuilder.create().build();
+        }
+
+        try {
+            return client.execute(httpReq);
+        } catch (IOException e) {
+            throw new RedfishException(String.format("Failed to execute POST request [URL: %s] due to exception.", url, e));
+        }
+    }
+
+    /**
+     *  Returns the proper URL path for the given Redfish command ({@link RedfishCmdType}).
+     */
+    private String getRequestPathForCommand(RedfishCmdType cmd, String resourceId) {
+        switch (cmd) {
+        case GetSystemId:
+            return SYSTEMS_URL_PATH;
+        case GetPowerState:
+            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);
+        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);
+        default:
+            throw new RedfishException(String.format("Redfish client does not support command '%s'.", cmd));
+        }
+    }
+
+    /**
+     * Validates the host address. It needs to be either a valid host domain name, or a valid IP address (IPv6 or IPv4).
+     */
+    protected String validateAddressAndPrepareForUrl(String hostAddress) {
+        if (NetUtils.isValidIp6(hostAddress)) {
+            return String.format("[%s]", hostAddress);
+        } else if (NetUtils.isValidIp4(hostAddress)) {
+            return hostAddress;
+        } else if (InternetDomainName.isValid(hostAddress)) {
+            return hostAddress;
+        } else {
+            throw new RedfishException(String.format("Redfish host address '%s' is not a valid IPv4 or IPv6 address", hostAddress));

Review comment:
       This Exception doesn't say what the code does. A valid hostname is also accepted. Not only IPv4 or IPv6 addresses.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] wido commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
wido commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-665553410


   @DaanHoogland The problem is that for Redfish testing (and IPMI) you need a physical machine do test this on.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] rhtyd commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
rhtyd commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-666121482


   I think like many other plugins, this would fall under 3rd party support due to lack of simulator/hardware. The ipmi specific plugin uses a ipmi-simulator library. I'm okay to merge it as is.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] GabrielBrascher commented on a change in pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
GabrielBrascher commented on a change in pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#discussion_r445092179



##########
File path: utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java
##########
@@ -0,0 +1,378 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package org.apache.cloudstack.utils.redfish;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+
+import com.cloud.utils.nio.TrustAllManager;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.protocol.HTTP;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.net.NetUtils;
+import com.google.common.net.InternetDomainName;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Provides support to a set of REST requests that can be sent to a Redfish Server. </br>
+ * RedfishClient allows to gather the server Power State, and execute Reset
+ * actions such as 'On', 'ForceOff', 'GracefulShutdown', 'GracefulRestart' etc.
+ */
+public class RedfishClient {
+
+    private static final Logger LOGGER = Logger.getLogger(RedfishClient.class);
+
+    private String username;
+    private String password;
+    private boolean useHttps;
+    private boolean ignoreSsl;
+
+    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 EXPECTED_HTTP_STATUS = "2XX";
+
+    /**
+     * Redfish Command type: </br>
+     * <b>ComputerSystemReset:</b> execute Redfish reset commands ({@link RedfishResetCmd}). </br>
+     * <b>GetSystemId:</b> get the system ID. </br>
+     * <b>GetPowerState:</b> used for get the system power state. </br>
+     */
+    public enum
+    RedfishCmdType {
+        ComputerSystemReset, GetSystemId, GetPowerState
+    }
+
+    /**
+     * Redfish System Power State: </br>
+     * <b>Off:</b> The state is powered Off. </br>
+     * <b>On:</b> The state is powered On. </br>
+     * <b>PoweringOff:</b> A temporary state between On and Off. </br>
+     * <b>PoweringOn:</b> A temporary state between Off and On.
+     */
+    public enum RedfishPowerState {
+        On, Off, PoweringOn, PoweringOff
+    }
+
+    /**
+     * <ul>
+     * <li><b>ForceOff:</b> Turn the unit off immediately (non-graceful shutdown).
+     * <li><b>ForceOn:</b> Turn the unit on immediately.
+     * <li><b>ForceRestart:</b> Perform an immediate (non-graceful) shutdown,
+     * followed by a restart.
+     * <li><b>GracefulRestart:</b> Perform a graceful shutdown followed by a restart
+     * of the system.
+     * <li><b>GracefulShutdown:</b> Perform a graceful shutdown and power off.
+     * <li><b>Nmi:</b> Generate a Diagnostic Interrupt (usually an NMI on x86
+     * systems) to cease normal operations, perform diagnostic actions and typically
+     * halt the system.
+     * <li><b>On:</b> Turn the unit on.
+     * <li><b>PowerCycle:</b> Perform a power cycle of the unit.
+     * <li><b>PushPowerButton:</b> Simulate the pressing of the physical power
+     * button on this unit.
+     * </ul>
+     */
+    public enum RedfishResetCmd {
+        ForceOff, ForceOn, ForceRestart, GracefulRestart, GracefulShutdown, Nmi, On, PowerCycle, PushPowerButton
+    }
+
+    public RedfishClient(String username, String password, boolean useHttps, boolean ignoreSsl) {
+        this.username = username;
+        this.password = password;
+        this.useHttps = useHttps;
+        this.ignoreSsl = ignoreSsl;
+    }
+
+    protected String buildRequestUrl(String hostAddress, RedfishCmdType cmd, String resourceId) {
+        String urlHostAddress = validateAddressAndPrepareForUrl(hostAddress);
+        String requestPath = getRequestPathForCommand(cmd, resourceId);
+
+        if (useHttps) {
+            return String.format("https://%s/%s", urlHostAddress, requestPath);
+        } else {
+            return String.format("http://%s/%s", urlHostAddress, requestPath);
+        }
+    }
+
+    /**
+     * Executes a GET request for the given URL address.
+     */
+    protected HttpResponse executeGetRequest(String url) {
+        URIBuilder builder = null;
+        HttpGet httpReq = null;
+
+        try {
+            builder = new URIBuilder(url);
+            httpReq = new HttpGet(builder.build());;
+            httpReq.addHeader(ACCEPT, APPLICATION_JSON);
+            String encoding = basicAuth(username, password);
+            httpReq.addHeader("Authorization", encoding);
+        } catch (URISyntaxException e) {
+            throw new RedfishException(String.format("Failed to create URI for GET request [URL: %s] due to exception.", url), e);
+        }
+
+        HttpClient client = null;
+        if (ignoreSsl) {
+            try {
+                client = ignoreSSLCertValidator();
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                throw new RedfishException(String.format("Failed to handle SSL Cert validator on GET request [URL: %s] due to exception.", url), e);
+            }
+        } else {
+            client = HttpClientBuilder.create().build();
+        }
+        try {
+            return client.execute(httpReq);
+        } catch (IOException e) {
+            throw new RedfishException(String.format("Failed to execute GET request [URL: %s] due to exception.", url), e);
+        }
+    }
+
+    private static String basicAuth(String username, String password) {
+        return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
+    }
+
+    /**
+     * Executes a POST request for the given URL address and Json object.
+     */
+    private HttpResponse executePostRequest(String url, JsonObject jsonToSend) {
+        HttpPost httpReq = null;
+        try {
+            URIBuilder builder = new URIBuilder(url);
+            httpReq = new HttpPost(builder.build());
+            httpReq.addHeader(ACCEPT, APPLICATION_JSON);
+            httpReq.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
+            String encoding = basicAuth(username, password);
+            httpReq.addHeader("Authorization", encoding);
+            httpReq.setEntity(new StringEntity(jsonToSend.toString()));
+        } catch (URISyntaxException | UnsupportedEncodingException e) {
+            throw new RedfishException(String.format("Failed to create URI for POST request [URL: %s] due to exception.", url), e);
+        }
+
+        HttpClient client = null;
+        if (ignoreSsl) {
+            try {
+                client = ignoreSSLCertValidator();
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                throw new RedfishException(String.format("Failed to handle SSL Cert validator on POST request [URL: %s] due to exception.", url), e);
+            }
+        } else {
+            client = HttpClientBuilder.create().build();
+        }
+
+        try {
+            return client.execute(httpReq);
+        } catch (IOException e) {
+            throw new RedfishException(String.format("Failed to execute POST request [URL: %s] due to exception.", url, e));
+        }
+    }
+
+    /**
+     *  Returns the proper URL path for the given Redfish command ({@link RedfishCmdType}).
+     */
+    private String getRequestPathForCommand(RedfishCmdType cmd, String resourceId) {
+        switch (cmd) {
+        case GetSystemId:
+            return SYSTEMS_URL_PATH;
+        case GetPowerState:
+            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);
+        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);
+        default:
+            throw new RedfishException(String.format("Redfish client does not support command '%s'.", cmd));
+        }
+    }
+
+    /**
+     * Validates the host address. It needs to be either a valid host domain name, or a valid IP address (IPv6 or IPv4).
+     */
+    protected String validateAddressAndPrepareForUrl(String hostAddress) {
+        if (NetUtils.isValidIp6(hostAddress)) {
+            return String.format("[%s]", hostAddress);
+        } else if (NetUtils.isValidIp4(hostAddress)) {
+            return hostAddress;
+        } else if (InternetDomainName.isValid(hostAddress)) {
+            return hostAddress;
+        } else {
+            throw new RedfishException(String.format("Redfish host address '%s' is not a valid IPv4 or IPv6 address", hostAddress));

Review comment:
       Good point, @wido! I will update this soon




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] blueorangutan commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
blueorangutan commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-653734742


   Packaging result: ✔centos7 ✔debian. JID-1529


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] rhtyd merged pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
rhtyd merged pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] rhtyd commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
rhtyd commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-653725292


   @blueorangutan package


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] GabrielBrascher commented on a change in pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
GabrielBrascher commented on a change in pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#discussion_r445108884



##########
File path: utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java
##########
@@ -0,0 +1,378 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package org.apache.cloudstack.utils.redfish;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+
+import com.cloud.utils.nio.TrustAllManager;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.protocol.HTTP;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.net.NetUtils;
+import com.google.common.net.InternetDomainName;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Provides support to a set of REST requests that can be sent to a Redfish Server. </br>
+ * RedfishClient allows to gather the server Power State, and execute Reset
+ * actions such as 'On', 'ForceOff', 'GracefulShutdown', 'GracefulRestart' etc.
+ */
+public class RedfishClient {
+
+    private static final Logger LOGGER = Logger.getLogger(RedfishClient.class);
+
+    private String username;
+    private String password;
+    private boolean useHttps;
+    private boolean ignoreSsl;
+
+    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 EXPECTED_HTTP_STATUS = "2XX";
+
+    /**
+     * Redfish Command type: </br>
+     * <b>ComputerSystemReset:</b> execute Redfish reset commands ({@link RedfishResetCmd}). </br>
+     * <b>GetSystemId:</b> get the system ID. </br>
+     * <b>GetPowerState:</b> used for get the system power state. </br>
+     */
+    public enum
+    RedfishCmdType {
+        ComputerSystemReset, GetSystemId, GetPowerState
+    }
+
+    /**
+     * Redfish System Power State: </br>
+     * <b>Off:</b> The state is powered Off. </br>
+     * <b>On:</b> The state is powered On. </br>
+     * <b>PoweringOff:</b> A temporary state between On and Off. </br>
+     * <b>PoweringOn:</b> A temporary state between Off and On.
+     */
+    public enum RedfishPowerState {
+        On, Off, PoweringOn, PoweringOff
+    }
+
+    /**
+     * <ul>
+     * <li><b>ForceOff:</b> Turn the unit off immediately (non-graceful shutdown).
+     * <li><b>ForceOn:</b> Turn the unit on immediately.
+     * <li><b>ForceRestart:</b> Perform an immediate (non-graceful) shutdown,
+     * followed by a restart.
+     * <li><b>GracefulRestart:</b> Perform a graceful shutdown followed by a restart
+     * of the system.
+     * <li><b>GracefulShutdown:</b> Perform a graceful shutdown and power off.
+     * <li><b>Nmi:</b> Generate a Diagnostic Interrupt (usually an NMI on x86
+     * systems) to cease normal operations, perform diagnostic actions and typically
+     * halt the system.
+     * <li><b>On:</b> Turn the unit on.
+     * <li><b>PowerCycle:</b> Perform a power cycle of the unit.
+     * <li><b>PushPowerButton:</b> Simulate the pressing of the physical power
+     * button on this unit.
+     * </ul>
+     */
+    public enum RedfishResetCmd {
+        ForceOff, ForceOn, ForceRestart, GracefulRestart, GracefulShutdown, Nmi, On, PowerCycle, PushPowerButton
+    }
+
+    public RedfishClient(String username, String password, boolean useHttps, boolean ignoreSsl) {
+        this.username = username;
+        this.password = password;
+        this.useHttps = useHttps;
+        this.ignoreSsl = ignoreSsl;
+    }
+
+    protected String buildRequestUrl(String hostAddress, RedfishCmdType cmd, String resourceId) {
+        String urlHostAddress = validateAddressAndPrepareForUrl(hostAddress);
+        String requestPath = getRequestPathForCommand(cmd, resourceId);
+
+        if (useHttps) {
+            return String.format("https://%s/%s", urlHostAddress, requestPath);
+        } else {
+            return String.format("http://%s/%s", urlHostAddress, requestPath);
+        }
+    }
+
+    /**
+     * Executes a GET request for the given URL address.
+     */
+    protected HttpResponse executeGetRequest(String url) {
+        URIBuilder builder = null;
+        HttpGet httpReq = null;
+
+        try {
+            builder = new URIBuilder(url);
+            httpReq = new HttpGet(builder.build());;
+            httpReq.addHeader(ACCEPT, APPLICATION_JSON);
+            String encoding = basicAuth(username, password);
+            httpReq.addHeader("Authorization", encoding);
+        } catch (URISyntaxException e) {
+            throw new RedfishException(String.format("Failed to create URI for GET request [URL: %s] due to exception.", url), e);
+        }
+
+        HttpClient client = null;
+        if (ignoreSsl) {
+            try {
+                client = ignoreSSLCertValidator();
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                throw new RedfishException(String.format("Failed to handle SSL Cert validator on GET request [URL: %s] due to exception.", url), e);
+            }
+        } else {
+            client = HttpClientBuilder.create().build();
+        }
+        try {
+            return client.execute(httpReq);
+        } catch (IOException e) {
+            throw new RedfishException(String.format("Failed to execute GET request [URL: %s] due to exception.", url), e);
+        }
+    }
+
+    private static String basicAuth(String username, String password) {
+        return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
+    }
+
+    /**
+     * Executes a POST request for the given URL address and Json object.
+     */
+    private HttpResponse executePostRequest(String url, JsonObject jsonToSend) {
+        HttpPost httpReq = null;
+        try {
+            URIBuilder builder = new URIBuilder(url);
+            httpReq = new HttpPost(builder.build());
+            httpReq.addHeader(ACCEPT, APPLICATION_JSON);
+            httpReq.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON);
+            String encoding = basicAuth(username, password);
+            httpReq.addHeader("Authorization", encoding);
+            httpReq.setEntity(new StringEntity(jsonToSend.toString()));
+        } catch (URISyntaxException | UnsupportedEncodingException e) {
+            throw new RedfishException(String.format("Failed to create URI for POST request [URL: %s] due to exception.", url), e);
+        }
+
+        HttpClient client = null;
+        if (ignoreSsl) {
+            try {
+                client = ignoreSSLCertValidator();
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                throw new RedfishException(String.format("Failed to handle SSL Cert validator on POST request [URL: %s] due to exception.", url), e);
+            }
+        } else {
+            client = HttpClientBuilder.create().build();
+        }
+
+        try {
+            return client.execute(httpReq);
+        } catch (IOException e) {
+            throw new RedfishException(String.format("Failed to execute POST request [URL: %s] due to exception.", url, e));
+        }
+    }
+
+    /**
+     *  Returns the proper URL path for the given Redfish command ({@link RedfishCmdType}).
+     */
+    private String getRequestPathForCommand(RedfishCmdType cmd, String resourceId) {
+        switch (cmd) {
+        case GetSystemId:
+            return SYSTEMS_URL_PATH;
+        case GetPowerState:
+            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);
+        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);
+        default:
+            throw new RedfishException(String.format("Redfish client does not support command '%s'.", cmd));
+        }
+    }
+
+    /**
+     * Validates the host address. It needs to be either a valid host domain name, or a valid IP address (IPv6 or IPv4).
+     */
+    protected String validateAddressAndPrepareForUrl(String hostAddress) {
+        if (NetUtils.isValidIp6(hostAddress)) {
+            return String.format("[%s]", hostAddress);
+        } else if (NetUtils.isValidIp4(hostAddress)) {
+            return hostAddress;
+        } else if (InternetDomainName.isValid(hostAddress)) {
+            return hostAddress;
+        } else {
+            throw new RedfishException(String.format("Redfish host address '%s' is not a valid IPv4 or IPv6 address", hostAddress));

Review comment:
       @wido code has been updated to `Redfish host address '%s' is not a valid IPv4/IPv6 address nor a valid domain name.`




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] GabrielBrascher commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
GabrielBrascher commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-665250531


   @DaanHoogland you are right, docs should be updated indeed. I will work on that as well.
   
   > and can we create a template for nested testing
   
   Do you mean [@TestTemplate](https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/api/TestTemplate.html) from JUnit5?


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] rhtyd edited a comment on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
rhtyd edited a comment on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-654649216


   Regression tests LGTM, we can merge if anyone else can review and possibly share test results against a redfish BMC or simulator.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] rhtyd commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
rhtyd commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-653735721


   @blueorangutan test


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] DaanHoogland commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
DaanHoogland commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-665849276


   > @DaanHoogland The problem is that for Redfish testing (and IPMI) you need a physical machine do test this on.
   
   ok, basically means we'll have to rely on you people testing. life is a dog, possibly of female gender, or not.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] blueorangutan commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
blueorangutan commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-653735816


   @rhtyd a Trillian-Jenkins test job (centos7 mgmt + kvm-centos7) has been kicked to run smoke tests


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] blueorangutan commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
blueorangutan commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-653820485


   <b>Trillian test result (tid-1981)</b>
   Environment: kvm-centos7 (x2), Advanced Networking with Mgmt server 7
   Total time taken: 51642 seconds
   Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr4175-t1981-kvm-centos7.zip
   Intermittent failure detected: /marvin/tests/smoke/test_vpc_redundant.py
   Smoke tests completed. 82 look OK, 1 have error(s)
   Only failed tests results shown below:
   
   
   Test | Result | Time (s) | Test File
   --- | --- | --- | ---
   test_03_create_redundant_VPC_1tier_2VMs_2IPs_2PF_ACL_reboot_routers | `Failure` | 481.95 | test_vpc_redundant.py
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] DaanHoogland commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
DaanHoogland commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-665547812


   > Do you mean [@TestTemplate](https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/api/TestTemplate.html) from JUnit5?
   
   no, nice link though, @GabrielBrascher . What i mean is a template VM that can serve as hypervisor for testing in. virtual nested environment.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] blueorangutan commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
blueorangutan commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-653725357


   @rhtyd a Jenkins job has been kicked to build packages. I'll keep you posted as I make progress.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [cloudstack] rhtyd commented on pull request #4175: Redfish Client & Redfish OOBM Driver

Posted by GitBox <gi...@apache.org>.
rhtyd commented on pull request #4175:
URL: https://github.com/apache/cloudstack/pull/4175#issuecomment-654649216


   Test LGTM, we can merge if anyone else can review and possibly share test results against a redfish BMC or simulator.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org