package org.ow2.choreos.ee.nodes.cloudprovider;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.log4j.Logger;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.ec2.domain.InstanceType;
import org.ow2.choreos.invoker.Invoker;
import org.ow2.choreos.invoker.InvokerException;
import org.ow2.choreos.invoker.InvokerFactory;
import org.ow2.choreos.nodes.NodeNotCreatedException;
import org.ow2.choreos.nodes.NodeNotDestroyed;
import org.ow2.choreos.nodes.NodeNotFoundException;
import org.ow2.choreos.nodes.datamodel.CloudNode;
import org.ow2.choreos.nodes.datamodel.NodeSpec;
import org.ow2.choreos.nodes.datamodel.ResourceImpact;
import com.google.common.collect.Iterables;
public abstract class JCloudsCloudProvider implements CloudProvider {
private static final String CREATION_TASK_NAME = "NODE_CREATION";
private static final String DELETION_TASK_NAME = "NODE_DELETION";
protected String identity, credential, provider;
protected Properties properties;
private Logger logger = Logger.getLogger(JCloudsCloudProvider.class);
protected abstract String getDefaultImageId();
protected abstract String getHardwareId();
protected abstract String getUserName();
protected abstract String getUserPrivateKey();
protected abstract void configureTemplateOptions(TemplateOptions templateOptions);
protected abstract String getNodeIp(NodeMetadata nodeMetadata);
@Override
public CloudNode createNode(NodeSpec nodeSpec) throws NodeNotCreatedException {
logger.debug("Creating node...");
CreateNodeTask task = new CreateNodeTask(nodeSpec);
InvokerFactory<CloudNode> factory = new InvokerFactory<CloudNode>();
Invoker<CloudNode> invoker = factory.geNewInvokerInstance(CREATION_TASK_NAME, task);
try {
CloudNode node = invoker.invoke();
return node;
} catch (InvokerException e) {
e.getCause().printStackTrace();
throw new NodeNotCreatedException();
}
}
protected ComputeService getComputeService() {
ContextBuilder builder = ContextBuilder.newBuilder(provider).credentials(identity, credential)
.overrides(properties);
ComputeService compute = null;
compute = builder.buildView(ComputeServiceContext.class).getComputeService();
return compute;
}
protected CloudNode getCloudNodeFromMetadata(NodeMetadata nodeMetadata) {
CloudNode node = new CloudNode();
node.setIp(getNodeIp(nodeMetadata));
node.setHostname(nodeMetadata.getName());
node.setSo(nodeMetadata.getOperatingSystem().getName());
node.setId(nodeMetadata.getId());
node.setImage(nodeMetadata.getImageId());
node.setState(nodeMetadata.getStatus().ordinal());
node.setUser(getUserName());
node.setPrivateKey(getUserPrivateKey());
return node;
}
protected Template getTemplate(ComputeService client, NodeSpec nodeSpec) {
String imageId = nodeSpec.getImage();
if (imageId == null || imageId.isEmpty()) {
imageId = getDefaultImageId();
}
String hardwareId = getHardwareId();
logger.info("Creating Template with image ID: " + imageId + "; hardware ID: " + hardwareId);
TemplateBuilder builder = client.templateBuilder().imageId(imageId);
builder.hardwareId(hardwareId);
Template template = null;
try {
template = builder.build();
} catch (Exception e) {
logger.error("Could not build template because: " + e.getMessage());
}
configureTemplateOptions(template.getOptions());
return template;
}
@SuppressWarnings("unused")
private String getInstanceTypeFromResourceImpact(ResourceImpact resourceImpact) {
String defaultImage = InstanceType.M1_SMALL;
if (resourceImpact != null && resourceImpact.getMemory() != null) {
switch (resourceImpact.getMemory()) {
case SMALL:
return InstanceType.M1_SMALL;
case MEDIUM:
return InstanceType.M1_MEDIUM;
case LARGE:
return InstanceType.M1_LARGE;
default:
return defaultImage;
}
}
return defaultImage;
}
@Override
public CloudNode getNode(String nodeId) throws NodeNotFoundException {
ComputeService client = getComputeService();
try {
NodeMetadata nodeMetadata = client.getNodeMetadata(nodeId);
CloudNode node = getCloudNodeFromMetadata(nodeMetadata);
client.getContext().close();
return node;
} catch (Exception e) {
throw new NodeNotFoundException(nodeId);
}
}
@Override
public List<CloudNode> getNodes() {
List<CloudNode> nodeList = new ArrayList<CloudNode>();
ComputeService client = getComputeService();
Set<? extends ComputeMetadata> cloudNodes = client.listNodes();
for (ComputeMetadata computeMetadata : cloudNodes) {
NodeMetadata nodeMetadata = client.getNodeMetadata(computeMetadata.getId());
CloudNode node = getCloudNodeFromMetadata(nodeMetadata);
if (node.getState() != 1 && node.hasIp()) {
nodeList.add(node);
}
}
client.getContext().close();
return nodeList;
}
@Override
public void destroyNode(String nodeId) throws NodeNotDestroyed {
DestroyNodeTask task = new DestroyNodeTask(nodeId);
InvokerFactory<Void> factory = new InvokerFactory<Void>();
Invoker<Void> invoker = factory.geNewInvokerInstance(DELETION_TASK_NAME, task);
try {
invoker.invoke();
} catch (InvokerException e) {
throw new NodeNotDestroyed(nodeId);
}
}
@Override
public CloudNode createOrUseExistingNode(NodeSpec nodeSpec) throws NodeNotCreatedException {
List<CloudNode> nodes = this.getNodes();
if (nodes.size() > 0)
return nodes.get(0);
else
return createNode(nodeSpec);
}
private class CreateNodeTask implements Callable<CloudNode> {
NodeSpec nodeSpec;
public CreateNodeTask(NodeSpec nodeSpec) {
this.nodeSpec = nodeSpec;
}
@Override
public CloudNode call() throws Exception {
ComputeService computeService = getComputeService();
try {
Template template = getTemplate(computeService, nodeSpec);
Set<? extends NodeMetadata> createdNodes = computeService.createNodesInGroup("default", 1, template);
NodeMetadata nodeMetadata = Iterables.get(createdNodes, 0);
CloudNode node = getCloudNodeFromMetadata(nodeMetadata);
if (!node.hasIp()) {
throw new NodeNotCreatedException("Could not retrieve IP from just created node.");
}
logger.debug(node + " created");
computeService.getContext().close();
return node;
} catch (RunNodesException e) {
logger.error("Node creation failed: " + e.getMessage());
throw new NodeNotCreatedException();
} catch (org.jclouds.rest.AuthorizationException e) {
logger.error("Authorization failed. Provided user doesn't have authorization to create a new node.");
throw new NodeNotCreatedException();
} catch (IllegalStateException e) {
logger.error(e);
throw new NodeNotCreatedException();
}
}
}
private class DestroyNodeTask implements Callable<Void> {
String nodeId;
public DestroyNodeTask(String nodeId) {
this.nodeId = nodeId;
}
@Override
public Void call() {
ComputeService client = getComputeService();
client.destroyNode(nodeId);
client.getContext().close();
return null;
}
}
}