/*
* Copyright 2004,2005 The Apache Software Foundation.
*
* Licensed 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.wso2.carbon.mediator.autoscale.ec2autoscale;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.core.SynapseEnvironment;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.ec2client.EC2Client;
import org.wso2.carbon.ec2client.EC2Exception;
import org.wso2.carbon.ec2client.data.*;
import org.wso2.carbon.mediator.autoscale.AutoscaleUtil;
import org.wso2.carbon.mediator.autoscale.LoadAnalyzerTask;
import org.wso2.carbon.utils.ServerConstants;
import sun.misc.BASE64Encoder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class EC2LoadAnalyzerTask extends LoadAnalyzerTask {
private static final Log log = LogFactory.getLog(EC2LoadAnalyzerTask.class);
private static EC2InstanceManager instanceManager = new EC2InstanceManager();
private static final String CLIENT_REPOSITORY_LOCATION = "Axis2Config.ClientRepositoryLocation";
private static final String CLIENT_AXIS2_XML_LOCATION = "Axis2Config.clientAxis2XmlLocation";
private static final String INSTANCE_MGT_WS_ENDPOINT = "InstanceMgtWSEndpoint";
private static boolean wasAddressAssignedToMe = false;
private String pEc2PrivateKey;
private String pEc2Cert;
private String pElasticIP = System.getenv("ELASTIC_IP");
private ConfigurationContext clientConfigContext;
/*
Load Balanced Service and the load balancer instance types
*/
private String pInstanceType = InstanceType.LARGE.getType();
private String pLoadBalancerInstanceType = InstanceType.SMALL.getType();
private String pInstanceAdditionalInfo;
private String pKey;
private String pImageId = System.getenv("ami_id");
private String pApplicationPayload;
private String pLoadBalancerPayload;
private List<Instance> runningApplicationInstances = new ArrayList<Instance>();
private List<Instance> pendingApplicationInstances = new ArrayList<Instance>();
private List<Instance> runningLBInstances = new ArrayList<Instance>();
private List<Instance> pendingLBInstances = new ArrayList<Instance>();
public void setApplicationPayload(String pApplicationPayload) {
this.pApplicationPayload = pApplicationPayload;
}
public void setLoadBalancerPayload(String pLoadBalancerPayload) {
this.pLoadBalancerPayload = pLoadBalancerPayload;
}
public void init(SynapseEnvironment synEnv) {
super.init(synEnv);
try {
createClientConfigContext();
} catch (EC2Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void execute() {
applicationGroupIds = extractGroupIds(pApplicationGroup);
loadBalancerGroupIds = extractGroupIds(getPLoadBalancerGroup());
if (!instanceManager.isEC2Client()) {
try {
createEC2Client();
setInstanceManager(instanceManager);
/*pick the current information and put in to Map and parse it to EC2InstanceManager methods*/
setRunInstanceDataforEC2();
calculateRunningAndPendingInstances();
} catch (EC2Exception e) {
autoscaleUtil.handleException(e.getMessage(), e);
}
} else {
try {
createEC2Client();
} catch (EC2Exception e) {
autoscaleUtil.handleException(e.getMessage(), e);
}
setInstanceManager(instanceManager);
try {
calculateRunningAndPendingInstances();
} catch (EC2Exception e) {
autoscaleUtil.handleException(e.getMessage(), e);
}
}
super.execute();
}
private void createClientConfigContext() throws EC2Exception {
// we can not use the same synapse configuration context object here.
// synapse configuration context uses its nhttp transport which can not
// use with the axis2 client
//TODO Use the clienSide ConfigContext from the OSGi service
ServerConfiguration serverConfig = ServerConfiguration.getInstance();
String clientRepositoryLocation =
serverConfig.getFirstProperty(CLIENT_REPOSITORY_LOCATION);
String clientAxis2XmlLocation =
serverConfig.getFirstProperty(CLIENT_AXIS2_XML_LOCATION);
try {
clientConfigContext =
ConfigurationContextFactory.createConfigurationContextFromFileSystem(
clientRepositoryLocation, clientAxis2XmlLocation);
} catch (AxisFault e) {
throw new EC2Exception("Cannot create new client ConfigurationContext for EC2 client", e);
}
clientConfigContext.setProperty(ServerConstants.WORK_DIR,
System.getProperty("axis2.work.dir"));
}
private void createEC2Client() throws EC2Exception {
ServerConfiguration serverConfig = ServerConfiguration.getInstance();
String instanceMgtWSEndpoint =
serverConfig.getFirstProperty(INSTANCE_MGT_WS_ENDPOINT);
instanceManager.setNonReplicapableProperty(getConfigCtx()); // TODO FIXME: Why is this needed
EC2Client ec2Client = new EC2Client(clientConfigContext, pEc2PrivateKey, pEc2Cert);
if (instanceMgtWSEndpoint == null || instanceMgtWSEndpoint.length() == 0) {
instanceManager.setEc2Client(ec2Client);
} else {
instanceManager.setEc2Client(ec2Client);
}
}
private void calculateRunningAndPendingInstances() throws EC2Exception {
//Calculate running and pending instances
List<Instance> instances = instanceManager.describeInstances();
for (Instance instance : instances) {
if (instance.getImage().getImageId().equals(pImageId)) {
//Calculating Application instances
if (containGroupIds(applicationGroupIds, instance.getGroupIds())) {
if (instance.getCurrentState().equals(InstanceState.RUNNING)) {
runningApplicationInstances.add(instance);
} else if (instance.getCurrentState().equals(InstanceState.PENDING)) {
pendingApplicationInstances.add(instance);
}
}
//Calculating LB instances
if (containGroupIds(loadBalancerGroupIds, instance.getGroupIds())) {
if (instance.getCurrentState().equals(InstanceState.RUNNING)) {
runningLBInstances.add(instance);
} else if (instance.getCurrentState().equals(InstanceState.PENDING)) {
pendingLBInstances.add(instance);
}
}
}
}
}
/**
* Carry out a sanity check. Checks whether the system meets the minimum requirements
*
* @return whether the caller has to continue execution or not.
* true - continue execution, false - abort execution
* @throws EC2Exception IF an error occurs while communicating with EC2
*/
public boolean doSanityCheck() throws EC2Exception {
// Assign the elastic IP to itself, if not already assigned
if (!wasAddressAssignedToMe) {
List<Address> addressList = instanceManager.describeAddresses(new String[]{pElasticIP});
String localInstanceId = System.getenv("instance_id");
// Associate this address with this instance only if any other instance has not assigned it to itself
Instance instance = null;
if (addressList.size() > 0) {
instance = addressList.get(0).getInstance();
} else {
autoscaleUtil.handleException("Elastic IP address " + pElasticIP + " has not been reserved");
}
if ((instance == null) ||
!(instance.getCurrentState().equals(InstanceState.RUNNING) ||
instance.getCurrentState().equals(InstanceState.PENDING))) {
// if the instance is not running or pending we assume that the instance has die
instanceManager.associateAddress(localInstanceId, pElasticIP);
wasAddressAssignedToMe = true;
// if a new IPAddress was assigned to this instance,
// it needs to create a new client since the IP address has changed
createEC2Client();
log.info("Associated Elastic IP " + pElasticIP + " with local instance " + localInstanceId);
} else if (!instance.getInstanceId().equals(localInstanceId)) { // Address assigned to a running instance
return false; // This is not the primary load balancer. Hence, need not continue
}
}
// Recheck whether this instance is still the primary LB, if it assigned the address to itself
// if (wasAddressAssignedToMe) {
// addressList = imanager.describeAddresses(new String[]{pElasticIP});
// if (!addressList.get(0).getInstance().getInstanceId().equals(localInstanceId)) {
// return false;
// }
// }
//TODO LB instance is 1 for this stratos release
// Check the min number of load balancer instances
int currentLoadBalancerInstances = runningLBInstances.size() + pendingLBInstances.size();
// if (log.isDebugEnabled()) {
// log.info("Current LB instances=" + currentLoadBalancerInstances);
// log.info("Min LB instances=" + getPMinLoadBalancerInstances());
// }
if (currentLoadBalancerInstances < getMinLoadBalancerInstances()) {
log.warn("Sanity check failed. Min LB instances is: " + currentLoadBalancerInstances +
". Specified min LB instances is: " + getMinLoadBalancerInstances());
int diff = getMinLoadBalancerInstances() - currentLoadBalancerInstances;
// Launch diff number of LB instances
log.info("Launching " + diff + " LB instances");
instanceManager.runInstances(pImageId,
diff,
InstanceType.getTypeFromString(pLoadBalancerInstanceType),
pKey,
loadBalancerGroupIds,
pInstanceAdditionalInfo,
getLoadBalancerUserData(),
new AvailabilityZone(getAvailabilityZone(), ""));
return false;
}
// Check the min number of Axis2 instances
int currentAxis2Instances = runningApplicationInstances.size() + pendingApplicationInstances.size();
// if (log.isDebugEnabled()) {
// log.info("Current Axis2 instances=" + currentAxis2Instances);
// log.info("Min Axis2 instances=" + getPMinAppInstances());
// }
if (currentAxis2Instances < getPMinAppInstances()) {
log.warn("Sanity check failed. Min Axis2 instances is: " + currentAxis2Instances +
". Specified min Axis2 instances is: " + getPMinAppInstances());
int diff = getPMinAppInstances() - currentAxis2Instances;
// Launch diff number of axis2 instances
log.info("Launching " + diff + " Axis2 instances");
instanceManager.runInstances(pImageId,
diff,
InstanceType.getTypeFromString(pInstanceType),
pKey,
applicationGroupIds,
pInstanceAdditionalInfo,
getAppUserData(),
new AvailabilityZone(getAvailabilityZone(), ""));
}
//TODO: Even if an instance is running, Axis2 may not be running, handle that situ
return true;
}
public void setEc2PrivateKey(String ec2PrivateKey) {
this.pEc2PrivateKey = ec2PrivateKey;
}
public void setEc2Cert(String ec2Cert) {
this.pEc2Cert = ec2Cert;
}
public void setInstanceType(String pInstanceType) {
this.pInstanceType = pInstanceType;
}
public void setLoadBalancerInstanceType(String pLoadBalancerInstanceType) {
this.pLoadBalancerInstanceType = pLoadBalancerInstanceType;
}
public void setInstanceAdditionalInfo(String instanceAdditionalInfo) {
this.pInstanceAdditionalInfo = instanceAdditionalInfo;
}
public void setKey(String key) {
this.pKey = key;
}
public void setElasticIP(String elasticIP) {
if (this.pElasticIP == null) {
this.pElasticIP = autoscaleUtil.replaceVariables(elasticIP);
}
}
public List<Instance> getRunningApplicationInstances() throws Exception {
// List applicationInstances = new ArrayList<Instance>();
// List<Instance> instances = getInstanceManager().describeInstances();
//
// for (Instance instance : instances) {
// if (instance.getImage().getImageId().equals(pImageId) &&
// instance.getCurrentState().equals(InstanceState.RUNNING) &&
// containGroupIds(applicationGroupIds, instance.getGroupIds())) {
// applicationInstances.add(instance);
// }
// }
// return applicationInstances;
return runningApplicationInstances;
}
public List<Instance> getPendingApplicationInstances() throws Exception {
// List<Instance> applicationInstances = new ArrayList<Instance>();
// List<Instance> instances = getInstanceManager().describeInstances();
// for (Instance instance : instances) {
// if (instance.getImage().getImageId().equals(pImageId) &&
// instance.getCurrentState().equals(InstanceState.PENDING) &&
// containGroupIds(applicationGroupIds, instance.getGroupIds())) {
// applicationInstances.add(instance);
// }
// }
// return applicationInstances;
return pendingApplicationInstances;
}
// private List<Instance> getRunningAndPendingInstances(String[] groupIds) throws EC2Exception {
// List<Instance> applicationInstances = new ArrayList<Instance>();
// List<Instance> instances = imanager.describeInstances();
// for (Instance instance : instances) {
// if (instance.getImage().getImageId().equals(pImageId) &&
// (instance.getCurrentState().equals(InstanceState.RUNNING) ||
// instance.getCurrentState().equals(InstanceState.PENDING)) &&
// containGroupIds(groupIds, instance.getGroupIds())) {
// applicationInstances.add(instance);
// }
// }
// return applicationInstances;
// }
private void setRunInstanceDataforEC2() throws EC2Exception {
if (runInstanceData == null) {
runInstanceData = new HashMap();
runInstanceData.put("ImageId", pImageId);
runInstanceData.put("InstancesPerScaleUp", getPInstancesPerScaleUp());
runInstanceData.put("InstanceType", InstanceType.getTypeFromString(pInstanceType));
runInstanceData.put("Key", pKey);
runInstanceData.put("ApplicationGroupIds", applicationGroupIds);
runInstanceData.put("InstanceAdditionalInfo", pInstanceAdditionalInfo);
runInstanceData.put("UserData", getAppUserData());
runInstanceData.put("AvailabilityZone", new AvailabilityZone(getAvailabilityZone(), ""));
}
}
public UserData getAppUserData() throws EC2Exception {
UserData userData = new UserData();
try {
byte[] bytes = AutoscaleUtil.getBytesFromFile(new File(pApplicationPayload));
BASE64Encoder encoder = new BASE64Encoder();
String userDataStr = encoder.encode(bytes);
userData.setData(userDataStr);
} catch (IOException e) {
autoscaleUtil.handleException("Cannot read data from payload file", e);
}
return userData;
}
public UserData getLoadBalancerUserData() throws EC2Exception {
UserData userData = new UserData();
try {
byte[] bytes = AutoscaleUtil.getBytesFromFile(new File(pLoadBalancerPayload));
BASE64Encoder encoder = new BASE64Encoder();
String userDataStr = encoder.encode(bytes);
userData.setData(userDataStr);
} catch (IOException e) {
autoscaleUtil.handleException("Cannot read data from payload file", e);
}
return userData;
}
}