You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by gi...@git.apache.org on 2017/10/10 14:03:08 UTC

[GitHub] rafaelweingartner commented on a change in pull request #2284: CLOUDSTACK-10103: Cloudian Connector for CloudStack

rafaelweingartner commented on a change in pull request #2284: CLOUDSTACK-10103: Cloudian Connector for CloudStack
URL: https://github.com/apache/cloudstack/pull/2284#discussion_r143734478
 
 

 ##########
 File path: plugins/integrations/cloudian/src/org/apache/cloudstack/cloudian/client/CloudianClient.java
 ##########
 @@ -0,0 +1,349 @@
+// 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.cloudian.client;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.utils.security.SSLUtils;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+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.protocol.HttpClientContext;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+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.BasicCredentialsProvider;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.nio.TrustAllManager;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Strings;
+
+public class CloudianClient {
+    private static final Logger LOG = Logger.getLogger(CloudianClient.class);
+
+    private final HttpClient httpClient;
+    private final HttpClientContext httpContext;
+    private final String adminApiUrl;
+
+    public CloudianClient(final String host, final Integer port, final String scheme, final String username, final String password, final boolean validateSSlCertificate, final int timeout) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
+        final CredentialsProvider provider = new BasicCredentialsProvider();
+        provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
+        final HttpHost adminHost = new HttpHost(host, port, scheme);
+        final AuthCache authCache = new BasicAuthCache();
+        authCache.put(adminHost, new BasicScheme());
+
+        this.adminApiUrl = adminHost.toURI();
+        this.httpContext = HttpClientContext.create();
+        this.httpContext.setCredentialsProvider(provider);
+        this.httpContext.setAuthCache(authCache);
+
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(timeout * 1000)
+                .setConnectionRequestTimeout(timeout * 1000)
+                .setSocketTimeout(timeout * 1000)
+                .build();
+
+        if (!validateSSlCertificate) {
+            final SSLContext sslcontext = SSLUtils.getSSLContext();
+            sslcontext.init(null, new X509TrustManager[]{new TrustAllManager()}, new SecureRandom());
+            final SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE);
+            this.httpClient = HttpClientBuilder.create()
+                    .setDefaultCredentialsProvider(provider)
+                    .setDefaultRequestConfig(config)
+                    .setSSLSocketFactory(factory)
+                    .build();
+        } else {
+            this.httpClient = HttpClientBuilder.create()
+                    .setDefaultCredentialsProvider(provider)
+                    .setDefaultRequestConfig(config)
+                    .build();
+        }
+    }
+
+    private void checkAuthFailure(final HttpResponse response) {
+        if (response != null && response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+            final Credentials credentials = httpContext.getCredentialsProvider().getCredentials(AuthScope.ANY);
+            LOG.error("Cloudian admin API authentication failed, please check Cloudian configuration. Admin auth principal=" + credentials.getUserPrincipal() + ", password=" + credentials.getPassword() + ", API url=" + adminApiUrl);
+            throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, "Cloudian backend API call unauthorized, please ask your administrator to fix integration issues.");
+        }
+    }
+
+    private void checkResponseOK(final HttpResponse response) {
+        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT) {
+            LOG.debug("Requested Cloudian resource does not exist");
+            return;
+        }
+        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK && response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to find the requested resource and get valid response from Cloudian backend API call, please ask your administrator to diagnose and fix issues.");
+        }
+    }
+
+    private void checkResponseTimeOut(final Exception e) {
+        if (e instanceof ConnectTimeoutException || e instanceof SocketTimeoutException) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "Operation timed out, please try again.");
+        }
+    }
+
+    private HttpResponse delete(final String path) throws IOException {
+        final HttpResponse response = httpClient.execute(new HttpDelete(adminApiUrl + path), httpContext);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    private HttpResponse get(final String path) throws IOException {
+        final HttpResponse response = httpClient.execute(new HttpGet(adminApiUrl + path), httpContext);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    private HttpResponse post(final String path, final Object item) throws IOException {
+        final ObjectMapper mapper = new ObjectMapper();
+        final String json = mapper.writeValueAsString(item);
+        final StringEntity entity = new StringEntity(json);
+        final HttpPost request = new HttpPost(adminApiUrl + path);
+        request.setHeader("Content-type", "application/json");
+        request.setEntity(entity);
+        final HttpResponse response = httpClient.execute(request, httpContext);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    private HttpResponse put(final String path, final Object item) throws IOException {
+        final ObjectMapper mapper = new ObjectMapper();
+        final String json = mapper.writeValueAsString(item);
+        final StringEntity entity = new StringEntity(json);
+        final HttpPut request = new HttpPut(adminApiUrl + path);
+        request.setHeader("Content-type", "application/json");
+        request.setEntity(entity);
+        final HttpResponse response = httpClient.execute(request, httpContext);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    ////////////////////////////////////////////////////////
+    //////////////// Public APIs: User /////////////////////
+    ////////////////////////////////////////////////////////
+
+    public boolean addUser(final CloudianUser user) {
+        if (user == null) {
+            return false;
+        }
+        LOG.debug("Adding Cloudian user: " + user);
+        try {
+            final HttpResponse response = put("/user", user);
+            return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
+        } catch (final IOException e) {
+            LOG.error("Failed to add Cloudian user due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return false;
+    }
+
+    public CloudianUser listUser(final String userId, final String groupId) {
+        if (Strings.isNullOrEmpty(userId) || Strings.isNullOrEmpty(groupId)) {
+            return null;
+        }
+        LOG.debug("Trying to find Cloudian user with id=" + userId + " and group id=" + groupId);
+        try {
+            final HttpResponse response = get(String.format("/user?userId=%s&groupId=%s", userId, groupId));
+            checkResponseOK(response);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT ||
+                    response.getEntity() == null ||
+                    response.getEntity().getContent() == null) {
+                return null;
+            }
+            final ObjectMapper mapper = new ObjectMapper();
+            return mapper.readValue(response.getEntity().getContent(), CloudianUser.class);
+        } catch (final IOException e) {
+            LOG.error("Failed to list Cloudian user due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return null;
+    }
+
+    public List<CloudianUser> listUsers(final String groupId) {
+        if (Strings.isNullOrEmpty(groupId)) {
+            return new ArrayList<>();
+        }
+        LOG.debug("Trying to list Cloudian users in group id=" + groupId);
+        try {
+            final HttpResponse response = get(String.format("/user/list?groupId=%s&userType=all&userStatus=active", groupId));
+            checkResponseOK(response);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT ||
+                    response.getEntity() == null ||
+                    response.getEntity().getContent() == null) {
+                return new ArrayList<>();
+            }
+            final ObjectMapper mapper = new ObjectMapper();
+            return Arrays.asList(mapper.readValue(response.getEntity().getContent(), CloudianUser[].class));
+        } catch (final IOException e) {
+            LOG.error("Failed to list Cloudian users due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return new ArrayList<>();
+    }
+
+    public boolean updateUser(final CloudianUser user) {
+        if (user == null) {
+            return false;
+        }
+        LOG.debug("Updating Cloudian user: " + user);
+        try {
+            final HttpResponse response = post("/user", user);
+            return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
+        } catch (final IOException e) {
+            LOG.error("Failed to update Cloudian user due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return false;
+    }
+
+    public boolean removeUser(final String userId, final String groupId) {
+        if (Strings.isNullOrEmpty(userId) || Strings.isNullOrEmpty(groupId)) {
+            return false;
+        }
+        LOG.debug("Removing Cloudian user with user id=" + userId + " in group id=" + groupId);
+        try {
+            final HttpResponse response = delete(String.format("/user?userId=%s&groupId=%s", userId, groupId));
+            return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
+        } catch (final IOException e) {
+            LOG.error("Failed to remove Cloudian user due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return false;
+    }
+
+    /////////////////////////////////////////////////////////
+    //////////////// Public APIs: Group /////////////////////
+    /////////////////////////////////////////////////////////
+
+    public boolean addGroup(final CloudianGroup group) {
 
 Review comment:
   What about throwing a runtime exception (a custom one) if it is not possible to add this `group` object?
   
   It feels more natural for a method to throw an exception if it fails than to return a Boolean indicating its status.
   
   The same applies for the `remove`, `list` (instead of returning null?) and `update` methods
 
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on 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


With regards,
Apache Git Services