/* * Copyright 2016 Dell Inc. * * Licensed 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 com.emc.storageos.driver.dellsc.scapi.rest; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthCache; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.ssl.AllowAllHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A basic REST client for SC API communication. */ public class RestClient implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(RestClient.class); private String baseUrl; private CloseableHttpClient httpClient; private HttpClientContext httpContext = null; /** * Instantiates a new Rest client. * * @param host Host name or IP address of the Dell Storage Manager server. * @param port Port the DSM data collector is listening on. * @param user The DSM user name to use. * @param password The DSM password. */ public RestClient(String host, int port, String user, String password) { this.baseUrl = String.format("https://%s:%d/api/rest", host, port); try { // Set up auth handling CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( new AuthScope(host, port), new UsernamePasswordCredentials(user, password)); AuthCache authCache = new BasicAuthCache(); BasicScheme basicAuth = new BasicScheme(); HttpHost target = new HttpHost(host, port, "https"); authCache.put(target, basicAuth); // Set up our context httpContext = HttpClientContext.create(); httpContext.setCookieStore(new BasicCookieStore()); httpContext.setAuthCache(authCache); // Create our HTTPS client SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { return true; } }).build(); SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); this.httpClient = HttpClients.custom() .setHostnameVerifier(new AllowAllHostnameVerifier()) .setDefaultCredentialsProvider(credsProvider) .setSSLSocketFactory(sslSocketFactory).build(); } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { // Hopefully default SSL handling is set up LOG.warn("Failed to configure HTTP handling, falling back to default handler."); LOG.debug("Config error: {}", e); this.httpClient = HttpClients.createDefault(); } } @Override public void close() { try { httpClient.close(); } catch (IOException e) { } httpContext = null; } /** * Format the REST endpoint URL. * * @param path The target path. * @return The full endpoint URL. */ private String formatUrl(String path) { // Make sure URL is formatted how we expect String urlPath = path; if (!path.startsWith("/")) { urlPath = String.format("/%s", path); } return String.format("%s%s", this.baseUrl, urlPath); } /** * Execute a REST call. * * @param request The REST request. * @return The results from the execution. */ private RestResult executeRequest(HttpRequestBase request) { RestResult result = null; request.addHeader("Accept", "application/json"); request.addHeader("x-dell-api-version", "2.0"); request.addHeader("Content-Type", "application/json; charset=utf-8"); CloseableHttpResponse response = null; try { response = httpClient.execute(request, httpContext); HttpEntity entity = response.getEntity(); result = new RestResult( request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), entity != null ? EntityUtils.toString(response.getEntity()) : ""); } catch (IOException e) { result = new RestResult(500, "Internal Failure", ""); LOG.warn(String.format("Error in API request: %s", e), e); } finally { try { if (response != null) { response.close(); } } catch (IOException e) { } } return result; } /** * Execute a GET REST call. * * @param path The relative path. * @return The execution result. */ public RestResult get(String path) { HttpGet httpGet = new HttpGet(formatUrl(path)); return executeRequest(httpGet); } /** * Execute a DELETE REST call. * * @param path The relative path. * @return The execution result. */ public RestResult delete(String path) { HttpDelete httpDelete = new HttpDelete(formatUrl(path)); return executeRequest(httpDelete); } /** * Execute a POST REST call. * * @param path The relative path. * @param payload The POST payload. * @return The execution result. */ public RestResult post(String path, String payload) { HttpPost httpPost = new HttpPost(formatUrl(path)); StringEntity entity = new StringEntity(payload, StandardCharsets.UTF_8); httpPost.setEntity(entity); return executeRequest(httpPost); } /** * Execute a PUT REST call. * * @param path The relative path. * @param payload The PUT payload. * @return The execution result. */ public RestResult put(String path, String payload) { HttpPut httpPut = new HttpPut(formatUrl(path)); httpPut.setEntity(new StringEntity(payload, StandardCharsets.UTF_8)); return executeRequest(httpPut); } }