package com.netflix.ice.basic;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.util.json.JSONArray;
import com.amazonaws.util.json.JSONException;
import com.amazonaws.util.json.JSONObject;
import com.google.common.collect.Lists;
import com.netflix.ice.common.ResourceService;
import com.netflix.ice.tag.Account;
import com.netflix.ice.tag.Product;
import com.netflix.ice.tag.Region;
/**
* A ResourceService which queries an https://github.com/Netflix/edda instance for the 'Usage' tag of instances for breaking down
* costs based on this tag.
*
* Recognizes configuration values "ice.eddaresourceservice.url" and "ice.eddaresourceservice.tag", i.e.
*
# Settings for our own Resource-Service
ice.eddaresourceservice.url=http://172.16.110.80:8080
ice.eddaresourceservice.tag=Usage
* Note: You will need to register the service in Bootstrap.groovy when ProcessorConfig and ReaderConfig are instantiated.
*
* TODO: There is currently no caching done, so there might be a lot of requests fired off to Edda!
*/
public class EddaResourceService extends ResourceService {
private static final ArrayList<Product> EC2_PRODUCTS = Lists.newArrayList(Product.ec2, Product.ec2_instance, Product.ebs);
//private static final ArrayList<Product> RDS_PRODUCTS = Lists.newArrayList(Product.rds);
//private static final ArrayList<Product> S3_PRODUCTS = Lists.newArrayList(Product.s3);
private final static Logger logger = LoggerFactory.getLogger(EddaResourceService.class);
@SuppressWarnings("unchecked")
private static List<List<Product>> productsWithResources =
Lists.<List<Product>>newArrayList(EC2_PRODUCTS/*, RDS_PRODUCTS*//*, S3_PRODUCTS*/);
// read from properties
protected String EDDA_ROOT_URL;
protected String EDDA_TAG_NAME;
//private final Properties prop;
public EddaResourceService(Properties prop) {
super();
//this.prop = prop;
EDDA_ROOT_URL = prop.getProperty("ice.eddaresourceservice.url", "http://localhost:18081/edda/api/v2/");
EDDA_TAG_NAME = prop.getProperty("ice.eddaresourceservice.tag", "Usage");
}
/* (non-Javadoc)
* @see com.netflix.ice.common.ResourceService#init()
*/
@Override
public void init() {
logger.info("Initializing...");
}
@Override
public String getResource(Account account, Region region, Product product, String resourceId, String[] lineItem,
long millisStart) {
// currently we support ec2
if(Product.ec2.equals(product) || Product.ec2_instance.equals(product)) {
if(StringUtils.isEmpty(resourceId)) {
logger.warn("Had empty resourceId");
return "Error";
}
try {
JSONArray instances = readInstanceArray();
boolean found = false;
for(int i = 0;i < instances.length();i++) {
String instance = instances.getString(i);
if(resourceId.equals(instance)) {
found = true;
break;
}
}
if(!found) {
logger.warn("Did not find resourceId in edda: " + resourceId);
return "Unknown";
}
InputStream stream = new URL(EDDA_ROOT_URL + "view/instances/" + resourceId).openStream();
final String json;
try {
json = IOUtils.toString(stream);
} finally {
stream.close();
}
JSONObject object = new JSONObject(json);
JSONArray tags = object.getJSONArray("tags");
for(int i = 0;i < tags.length();i++) {
JSONObject tag = tags.getJSONObject(i);
String key = tag.getString("key");
if(key.equals(EDDA_TAG_NAME)) {
String usage = tag.getString("value");
logger.debug("Found usage: " + usage + " for resource " + resourceId);
return usage;
}
}
logger.debug("Did not find tag 'Usage' for resource " + resourceId);
return "Unknown";
} catch (JSONException e) {
logger.warn("error parsing json", e);
return "Error";
} catch (MalformedURLException e) {
logger.warn("error parsing url", e);
return "Error";
} catch (IOException e) {
logger.warn("error fetching data from edda at " + EDDA_ROOT_URL, e);
return "Error";
}
}
logger.debug("Product: " + product + " not handled, resourceId: " + resourceId);
//logger.info("get resource for account " + account + " region " + region + " product " + product + " resource: " + resourceId + " lineItem: " + Arrays.toString(lineItem));
return super.getResource(account, region, product, resourceId, lineItem, millisStart);
}
/* (non-Javadoc)
* @see com.netflix.ice.common.ResourceService#getProductsWithResources()
*/
@Override
public List<List<Product>> getProductsWithResources() {
logger.info("Register for products: " + productsWithResources + "...");
return productsWithResources;
}
/* (non-Javadoc)
* @see com.netflix.ice.common.ResourceService#commit()
*/
@Override
public void commit() {
logger.info("Commit...");
}
protected JSONArray readInstanceArray() throws IOException, MalformedURLException, JSONException {
InputStream stream = new URL(EDDA_ROOT_URL + "view/instances").openStream();
final String json;
try {
json = IOUtils.toString(stream);
} finally {
stream.close();
}
JSONArray instances = new JSONArray(json);
return instances;
}
}