blob: 934087a59a5d91bfa28669135b7edace8672a54d [file] [log] [blame]
// 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.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
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 {
protected static Logger LOGGER = LogManager.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) {
LOGGER.error(e.getMessage());
return false;
} catch (IOException e) {
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) {
LOGGER.error(e.getMessage());
return null;
}
} else {
return null;
}
} catch (HttpException e) {
LOGGER.error(e.getMessage());
return null;
} catch (IOException e) {
LOGGER.error(e.getMessage());
return null;
} catch (ClassNotFoundException e) {
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) {
LOGGER.error(e.getMessage());
return null;
}
} else {
return null;
}
} catch (HttpException e) {
LOGGER.error(e.getMessage());
return null;
} catch (IOException e) {
LOGGER.error(e.getMessage());
return null;
} catch (ClassNotFoundException e) {
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) {
LOGGER.error(e.getMessage());
return null;
}
} else {
return null;
}
} catch (HttpException e) {
LOGGER.error(e.getMessage());
return null;
} catch (IOException e) {
LOGGER.error(e.getMessage());
return null;
} catch (ClassNotFoundException e) {
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) {
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) {
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) {
LOGGER.error(ex.getMessage());
return null;
}
}
}