| // 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.region; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectInputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.net.URLEncoder; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import javax.crypto.Mac; |
| import javax.crypto.spec.SecretKeySpec; |
| |
| import org.apache.commons.codec.binary.Base64; |
| import org.apache.commons.httpclient.HttpClient; |
| import org.apache.commons.httpclient.HttpException; |
| import org.apache.commons.httpclient.HttpMethod; |
| import org.apache.commons.httpclient.NameValuePair; |
| import org.apache.commons.httpclient.methods.GetMethod; |
| import org.apache.log4j.Logger; |
| |
| import com.thoughtworks.xstream.XStream; |
| import com.thoughtworks.xstream.io.xml.DomDriver; |
| |
| import com.cloud.domain.DomainVO; |
| import com.cloud.user.UserAccount; |
| import com.cloud.user.UserAccountVO; |
| |
| /** |
| * Utility class for making API calls between peer Regions |
| * |
| */ |
| public class RegionsApiUtil { |
| public static final Logger s_logger = Logger.getLogger(RegionsApiUtil.class); |
| |
| /** |
| * Makes an api call using region service end_point, api command and params |
| * @param region |
| * @param command |
| * @param params |
| * @return True, if api is successful |
| */ |
| protected static boolean makeAPICall(Region region, String command, List<NameValuePair> params) { |
| try { |
| String apiParams = buildParams(command, params); |
| String url = buildUrl(apiParams, region); |
| HttpClient client = new HttpClient(); |
| HttpMethod method = new GetMethod(url); |
| if (client.executeMethod(method) == 200) { |
| return true; |
| } else { |
| return false; |
| } |
| } catch (HttpException e) { |
| s_logger.error(e.getMessage()); |
| return false; |
| } catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return false; |
| } |
| } |
| |
| /** |
| * Makes an api call using region service end_point, api command and params |
| * Returns Account object on success |
| * @param region |
| * @param command |
| * @param params |
| * @return |
| */ |
| protected static RegionAccount makeAccountAPICall(Region region, String command, List<NameValuePair> params) { |
| try { |
| String url = buildUrl(buildParams(command, params), region); |
| HttpClient client = new HttpClient(); |
| HttpMethod method = new GetMethod(url); |
| if (client.executeMethod(method) == 200) { |
| InputStream is = method.getResponseBodyAsStream(); |
| //Translate response to Account object |
| XStream xstream = new XStream(new DomDriver()); |
| xstream.alias("account", RegionAccount.class); |
| xstream.alias("user", RegionUser.class); |
| xstream.aliasField("id", RegionAccount.class, "uuid"); |
| xstream.aliasField("name", RegionAccount.class, "accountName"); |
| xstream.aliasField("accounttype", RegionAccount.class, "type"); |
| xstream.aliasField("domainid", RegionAccount.class, "domainUuid"); |
| xstream.aliasField("networkdomain", RegionAccount.class, "networkDomain"); |
| xstream.aliasField("id", RegionUser.class, "uuid"); |
| xstream.aliasField("accountId", RegionUser.class, "accountUuid"); |
| try(ObjectInputStream in = xstream.createObjectInputStream(is);) { |
| return (RegionAccount) in.readObject(); |
| }catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } catch (HttpException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } catch (ClassNotFoundException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } |
| |
| /** |
| * Makes an api call using region service end_point, api command and params |
| * Returns Domain object on success |
| * @param region |
| * @param command |
| * @param params |
| * @return |
| */ |
| protected static RegionDomain makeDomainAPICall(Region region, String command, List<NameValuePair> params) { |
| try { |
| String url = buildUrl(buildParams(command, params), region); |
| HttpClient client = new HttpClient(); |
| HttpMethod method = new GetMethod(url); |
| if (client.executeMethod(method) == 200) { |
| InputStream is = method.getResponseBodyAsStream(); |
| XStream xstream = new XStream(new DomDriver()); |
| //Translate response to Domain object |
| xstream.alias("domain", RegionDomain.class); |
| xstream.aliasField("id", RegionDomain.class, "uuid"); |
| xstream.aliasField("parentdomainid", RegionDomain.class, "parentUuid"); |
| xstream.aliasField("networkdomain", DomainVO.class, "networkDomain"); |
| try(ObjectInputStream in = xstream.createObjectInputStream(is);) { |
| return (RegionDomain) in.readObject(); |
| }catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } catch (HttpException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } catch (ClassNotFoundException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } |
| |
| /** |
| * Makes an api call using region service end_point, api command and params |
| * Returns UserAccount object on success |
| * @param region |
| * @param command |
| * @param params |
| * @return |
| */ |
| protected static UserAccount makeUserAccountAPICall(Region region, String command, List<NameValuePair> params) { |
| try { |
| String url = buildUrl(buildParams(command, params), region); |
| HttpClient client = new HttpClient(); |
| HttpMethod method = new GetMethod(url); |
| if (client.executeMethod(method) == 200) { |
| InputStream is = method.getResponseBodyAsStream(); |
| XStream xstream = new XStream(new DomDriver()); |
| xstream.alias("useraccount", UserAccountVO.class); |
| xstream.aliasField("id", UserAccountVO.class, "uuid"); |
| try(ObjectInputStream in = xstream.createObjectInputStream(is);) { |
| return (UserAccountVO)in.readObject(); |
| } catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } catch (HttpException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } catch (IOException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } catch (ClassNotFoundException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } |
| |
| /** |
| * Builds parameters string with command and encoded param values |
| * @param command |
| * @param params |
| * @return |
| */ |
| protected static String buildParams(String command, List<NameValuePair> params) { |
| StringBuffer paramString = new StringBuffer("command=" + command); |
| Iterator<NameValuePair> iter = params.iterator(); |
| try { |
| while (iter.hasNext()) { |
| NameValuePair param = iter.next(); |
| if (param.getValue() != null && !(param.getValue().isEmpty())) { |
| paramString.append("&" + param.getName() + "=" + URLEncoder.encode(param.getValue(), "UTF-8")); |
| } |
| } |
| } catch (UnsupportedEncodingException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| return paramString.toString(); |
| } |
| |
| /** |
| * Build URL for api call using region end_point |
| * Parameters are sorted and signed using secret_key |
| * @param apiParams |
| * @param region |
| * @return |
| */ |
| private static String buildUrl(String apiParams, Region region) { |
| |
| String apiKey = ""; |
| String secretKey = ""; |
| String encodedApiKey; |
| try { |
| encodedApiKey = URLEncoder.encode(apiKey, "UTF-8"); |
| |
| List<String> sortedParams = new ArrayList<String>(); |
| sortedParams.add("apikey=" + encodedApiKey.toLowerCase()); |
| StringTokenizer st = new StringTokenizer(apiParams, "&"); |
| String url = null; |
| boolean first = true; |
| while (st.hasMoreTokens()) { |
| String paramValue = st.nextToken(); |
| String param = paramValue.substring(0, paramValue.indexOf("=")); |
| String value = paramValue.substring(paramValue.indexOf("=") + 1, paramValue.length()); |
| if (first) { |
| url = param + "=" + value; |
| first = false; |
| } else { |
| url = url + "&" + param + "=" + value; |
| } |
| sortedParams.add(param.toLowerCase() + "=" + value.toLowerCase()); |
| } |
| Collections.sort(sortedParams); |
| |
| //Construct the sorted URL and sign and URL encode the sorted URL with your secret key |
| String sortedUrl = null; |
| first = true; |
| for (String param : sortedParams) { |
| if (first) { |
| sortedUrl = param; |
| first = false; |
| } else { |
| sortedUrl = sortedUrl + "&" + param; |
| } |
| } |
| String encodedSignature = signRequest(sortedUrl, secretKey); |
| |
| String finalUrl = region.getEndPoint() + "?" + apiParams + "&apiKey=" + apiKey + "&signature=" + encodedSignature; |
| |
| return finalUrl; |
| |
| } catch (UnsupportedEncodingException e) { |
| s_logger.error(e.getMessage()); |
| return null; |
| } |
| } |
| |
| /** |
| * 1. Signs a string with a secret key using SHA-1 2. Base64 encode the result 3. URL encode the final result |
| * |
| * @param request |
| * @param key |
| * @return |
| */ |
| private static String signRequest(String request, String key) { |
| try { |
| Mac mac = Mac.getInstance("HmacSHA1"); |
| SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); |
| mac.init(keySpec); |
| mac.update(request.getBytes()); |
| byte[] encryptedBytes = mac.doFinal(); |
| return URLEncoder.encode(Base64.encodeBase64String(encryptedBytes), "UTF-8"); |
| } catch (Exception ex) { |
| s_logger.error(ex.getMessage()); |
| return null; |
| } |
| } |
| |
| } |