//
// typica - A client library for Amazon Web Services
// Copyright (C) 2007,2008,2009 Xerox Corporation
//
// 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 com.xerox.amazonws.ec2;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
import org.xml.sax.SAXException;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpException;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.xerox.amazonws.monitoring.StandardUnit;
import com.xerox.amazonws.monitoring.Statistics;
import com.xerox.amazonws.common.AWSException;
import com.xerox.amazonws.common.AWSQueryConnection;
import com.xerox.amazonws.typica.autoscale.jaxb.CreateAutoScalingGroupResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DeleteAutoScalingGroupResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DeleteTriggerResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DescribeTriggersResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DescribeScalingActivitiesResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DescribeScalingActivitiesResult;
import com.xerox.amazonws.typica.autoscale.jaxb.DescribeAutoScalingGroupsResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.CreateOrUpdateScalingTriggerResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.CreateLaunchConfigurationResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DeleteLaunchConfigurationResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DescribeLaunchConfigurationsResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.DescribeLaunchConfigurationsResult;
import com.xerox.amazonws.typica.autoscale.jaxb.LaunchConfigurations;
import com.xerox.amazonws.typica.autoscale.jaxb.SetDesiredCapacityResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.TerminateInstanceInAutoScalingGroupResponse;
import com.xerox.amazonws.typica.autoscale.jaxb.TerminateInstanceInAutoScalingGroupResult;
import com.xerox.amazonws.typica.autoscale.jaxb.UpdateAutoScalingGroupResponse;
/**
* A Java wrapper for the AutoScaling web services API
*/
public class AutoScaling extends AWSQueryConnection {
private static Log logger = LogFactory.getLog(AutoScaling.class);
/**
* Initializes the AutoScaling service with your AWS login information.
*
* @param awsAccessId The your user key into AWS
* @param awsSecretKey The secret string used to generate signatures for authentication.
*/
public AutoScaling(String awsAccessId, String awsSecretKey) {
this(awsAccessId, awsSecretKey, true);
}
/**
* Initializes the AutoScaling service with your AWS login information.
*
* @param awsAccessId The your user key into AWS
* @param awsSecretKey The secret string used to generate signatures for authentication.
* @param isSecure True if the data should be encrypted on the wire on the way to or from EC2.
*/
public AutoScaling(String awsAccessId, String awsSecretKey, boolean isSecure) {
this(awsAccessId, awsSecretKey, isSecure, "autoscaling.amazonaws.com");
}
/**
* Initializes the AutoScaling service with your AWS login information.
*
* @param awsAccessId The your user key into AWS
* @param awsSecretKey The secret string used to generate signatures for authentication.
* @param isSecure True if the data should be encrypted on the wire on the way to or from EC2.
* @param server Which host to connect to. Usually, this will be autoscaling.amazonaws.com
*/
public AutoScaling(String awsAccessId, String awsSecretKey, boolean isSecure,
String server)
{
this(awsAccessId, awsSecretKey, isSecure, server,
isSecure ? 443 : 80);
}
/**
* Initializes the AutoScaling service with your AWS login information.
*
* @param awsAccessId The your user key into AWS
* @param awsSecretKey The secret string used to generate signatures for authentication.
* @param isSecure True if the data should be encrypted on the wire on the way to or from EC2.
* @param server Which host to connect to. Usually, this will be autoscaling.amazonaws.com
* @param port Which port to use.
*/
public AutoScaling(String awsAccessId, String awsSecretKey, boolean isSecure,
String server, int port)
{
super(awsAccessId, awsSecretKey, isSecure, server, port);
ArrayList<String> vals = new ArrayList<String>();
vals.add("2009-05-15");
super.headers.put("Version", vals);
}
/**
* Create a launch configuration
*
* @param config the launch configuration
* @throws AutoScalingException wraps checked exceptions
*/
public void createLaunchConfiguration(LaunchConfiguration config) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("LaunchConfigurationName", config.getConfigName());
params.put("ImageId", config.getImageId());
//params.put("MinCount", "" + config.getMinCount());
//params.put("MaxCount", "" + config.getMaxCount());
byte[] userData = config.getUserData();
if (userData != null && userData.length > 0) {
params.put("UserData",
new String(Base64.encodeBase64(userData)));
}
params.put("AddressingType", "public");
String keyName = config.getKeyName();
if (keyName != null && !keyName.trim().equals("")) {
params.put("KeyName", keyName);
}
if (config.getSecurityGroup() != null) {
for(int i = 0; i < config.getSecurityGroup().size(); i++) {
params.put("SecurityGroup." + (i + 1), config.getSecurityGroup().get(i));
}
}
params.put("InstanceType", config.getInstanceType().getTypeId());
if (config.getAvailabilityZone() != null && !config.getAvailabilityZone().trim().equals("")) {
params.put("Placement.AvailabilityZone", config.getAvailabilityZone());
}
if (config.getKernelId() != null && !config.getKernelId().trim().equals("")) {
params.put("KernelId", config.getKernelId());
}
if (config.getRamdiskId() != null && !config.getRamdiskId().trim().equals("")) {
params.put("RamdiskId", config.getRamdiskId());
}
if (config.getBlockDevicemappings() != null) {
for(int i = 0; i < config.getBlockDevicemappings().size(); i++) {
BlockDeviceMapping bdm = config.getBlockDevicemappings().get(i);
params.put("BlockDeviceMapping." + (i + 1) + ".VirtualName",
bdm.getVirtualName());
params.put("BlockDeviceMapping." + (i + 1) + ".DeviceName",
bdm.getDeviceName());
}
}
HttpGet method = new HttpGet();
// CreateLaunchConfigurationResponse response =
makeRequestInt(method, "CreateLaunchConfiguration", params, CreateLaunchConfigurationResponse.class);
}
/**
* Delete a launch configuration
*
* @param configName the name of the configuration to delete
* @throws AutoScalingException wraps checked exceptions
*/
public void deleteLaunchConfiguration(String configName) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("LaunchConfigurationName", configName);
HttpGet method = new HttpGet();
// DeleteLaunchConfigurationResponse response =
makeRequestInt(method, "DeleteLaunchConfiguration", params, DeleteLaunchConfigurationResponse.class);
}
/**
* Describe the launch configurations that have been created
*
* @param configNames the names of the configurations to show, null for all
* @return A list of {@link LaunchConfiguration} configs
* @throws AutoScalingException wraps checked exceptions
*/
public List<LaunchConfiguration> describeLaunchConfigurations(List<String> configNames) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
if (configNames != null && configNames.size() > 0) {
int i = 0;
for (String name : configNames) {
params.put("LaunchConfigurationNames.member."+(i+1), name);
i++;
}
}
HttpGet method = new HttpGet();
DescribeLaunchConfigurationsResponse response =
makeRequestInt(method, "DescribeLaunchConfigurations", params, DescribeLaunchConfigurationsResponse.class);
List<com.xerox.amazonws.typica.autoscale.jaxb.LaunchConfiguration> result =
response.getDescribeLaunchConfigurationsResult().getLaunchConfigurations().getMembers();
List<LaunchConfiguration> ret = new ArrayList<LaunchConfiguration>();
for (com.xerox.amazonws.typica.autoscale.jaxb.LaunchConfiguration config : result) {
LaunchConfiguration newConfig = new LaunchConfiguration(config.getLaunchConfigurationName(),
config.getImageId(), 1, 1);
newConfig.setKeyName(config.getKeyName());
newConfig.setSecurityGroup(config.getSecurityGroups().getMembers());
newConfig.setUserData(config.getUserData().getBytes());
newConfig.setInstanceType(InstanceType.getTypeFromString(config.getInstanceType()));
newConfig.setKernelId(config.getKernelId());
newConfig.setRamdiskId(config.getRamdiskId());
List<BlockDeviceMapping> mappings = new ArrayList<BlockDeviceMapping>();
for (com.xerox.amazonws.typica.autoscale.jaxb.BlockDeviceMapping mapping : config.getBlockDeviceMappings().getMembers()) {
mappings.add(new BlockDeviceMapping(mapping.getVirtualName(), mapping.getDeviceName()));
}
newConfig.setBlockDevicemappings(mappings);
ret.add(newConfig);
}
return ret;
}
/**
* Terminates a running instance.
*
* @param instanceId An instance id
* @param shouldDecrement true of desired capacity should be decremented at the same time
* @return activity description
* @throws AutoScalingException wraps checked exceptions
*/
public Activity terminateInstancesInAutoScalingGroup(String instanceId, boolean shouldDecrement) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("InstanceId", instanceId);
params.put("ShouldDecrementDesiredCapacity", shouldDecrement?"true":"false");
HttpGet method = new HttpGet();
TerminateInstanceInAutoScalingGroupResponse response =
makeRequestInt(method, "TerminateInstanceInAutoScalingGroup", params, TerminateInstanceInAutoScalingGroupResponse.class);
TerminateInstanceInAutoScalingGroupResult result = response.getTerminateInstanceInAutoScalingGroupResult();
com.xerox.amazonws.typica.autoscale.jaxb.Activity activity = result.getActivity();
Activity ret = new Activity(activity.getActivityId(), activity.getDescription(),
activity.getCause(), activity.getStartTime().toString(),
activity.getEndTime().toString(), activity.getStatusCode(),
activity.getStatusMessage(),
activity.getProgress().intValue());
return ret;
}
/**
* Describes the autoScaling activities for a given group.
*
* @param activityIds activity ids used to filter the list, null for all
* @param autoScalingGroupName an auto scaling group name
* @return activity descriptions
* @throws AutoScalingException wraps checked exceptions
*/
public List<Activity> describeScalingActivities(List<String> activityIds, String autoScalingGroupName) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
if (activityIds != null && activityIds.size() > 0) {
int i = 0;
for (String id : activityIds) {
params.put("ActivityIds.member."+(i+1), id);
i++;
}
}
params.put("AutoScalingGroupName", autoScalingGroupName);
HttpGet method = new HttpGet();
List<Activity> ret = new ArrayList<Activity>();
String nextToken = null;
do {
if (nextToken != null) {
params.put("NextToken", nextToken);
}
DescribeScalingActivitiesResponse response =
makeRequestInt(method, "DescribeScalingActivities", params, DescribeScalingActivitiesResponse.class);
DescribeScalingActivitiesResult result = response.getDescribeScalingActivitiesResult();
List<com.xerox.amazonws.typica.autoscale.jaxb.Activity> activities =
result.getActivities().getMembers();
for (com.xerox.amazonws.typica.autoscale.jaxb.Activity activity : activities) {
Activity newActivity = new Activity(activity.getActivityId(), activity.getDescription(),
activity.getCause(), activity.getStartTime().toString(),
activity.getEndTime().toString(), activity.getStatusCode(),
activity.getStatusMessage(),
activity.getProgress().intValue());
ret.add(newActivity);
}
nextToken = result.getNextToken();
} while (nextToken != null);
return ret;
}
/**
* Creates a scaling trigger, or updates an existing one
*
* @param trigger the information about the trigger
* @throws AutoScalingException wraps checked exceptions
*/
public void createOrUpdateScalingTrigger(ScalingTrigger trigger) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("TriggerName", trigger.getName());
params.put("AutoScalingGroupName", trigger.getAutoScalingGroupName());
params.put("MeasureName", trigger.getMeasureName());
params.put("Statistic", trigger.getStatistic().getStatId());
Map<String, String> dimensions = trigger.getDimensions();
if (dimensions != null && dimensions.size() > 0) {
int i=0;
for (String key : dimensions.keySet()) {
String value = dimensions.get(key);
params.put("Dimensions.member."+(i+1)+".Name", key);
params.put("Dimensions.member."+(i+1)+".Value", value);
i++;
}
}
params.put("Period", ""+trigger.getPeriod());
if (trigger.getUnit() != null) {
params.put("Unit", trigger.getUnit().getUnitId());
}
String tmp = trigger.getCustomUnit();
if (tmp != null && !tmp.equals("")) {
params.put("CustomUnit", tmp);
}
params.put("LowerThreshold", ""+trigger.getLowerThreshold());
params.put("LowerBreachScaleIncrement", trigger.getLowerBreachScaleIncrement());
params.put("UpperThreshold", ""+trigger.getUpperThreshold());
params.put("UpperBreachScaleIncrement", trigger.getUpperBreachScaleIncrement());
params.put("BreachDuration", ""+trigger.getBreachDuration());
HttpGet method = new HttpGet();
// CreateOrUpdateScalingTriggerReponse response =
makeRequestInt(method, "CreateOrUpdateScalingTrigger", params, CreateOrUpdateScalingTriggerResponse.class);
}
/**
* Deletes a trigger
*
* @param triggerName An trigger name
* @param autoScalingGroupName a autoScaling group name
* @throws AutoScalingException wraps checked exceptions
*/
public void deleteTrigger(String triggerName, String autoScalingGroupName) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("TriggerName", triggerName);
params.put("AutoScalingGroupName", autoScalingGroupName);
HttpGet method = new HttpGet();
// DeleteTriggerResponse response =
makeRequestInt(method, "DeleteTrigger", params, DeleteTriggerResponse.class);
}
/**
* Describes the scaling triggers for a given group.
*
* @param autoScalingGroupName a autoScaling group name
* @return activity descriptions
* @throws AutoScalingException wraps checked exceptions
*/
public List<ScalingTrigger> describeTriggers(String autoScalingGroupName) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("AutoScalingGroupName", autoScalingGroupName);
HttpGet method = new HttpGet();
DescribeTriggersResponse response =
makeRequestInt(method, "DescribeTriggers", params, DescribeTriggersResponse.class);
List<com.xerox.amazonws.typica.autoscale.jaxb.Trigger> result =
response.getDescribeTriggersResult().getTriggers().getMembers();
List<ScalingTrigger> ret = new ArrayList<ScalingTrigger>();
for (com.xerox.amazonws.typica.autoscale.jaxb.Trigger trigger : result) {
Map<String, String> dimensions = new HashMap<String, String>();
List<com.xerox.amazonws.typica.autoscale.jaxb.Dimension> dims = trigger.getDimensions().getMembers();
for (com.xerox.amazonws.typica.autoscale.jaxb.Dimension dim : dims) {
dimensions.put(dim.getName(), dim.getValue());
}
ScalingTrigger newTrigger = new ScalingTrigger(trigger.getTriggerName(), trigger.getAutoScalingGroupName(),
trigger.getMeasureName(), Statistics.getTypeFromString(trigger.getStatistic()),
dimensions, trigger.getPeriod().intValue(),
StandardUnit.getTypeFromString(trigger.getUnit()), trigger.getCustomUnit(),
trigger.getLowerThreshold(),
trigger.getLowerBreachScaleIncrement(),
trigger.getUpperThreshold(),
trigger.getUpperBreachScaleIncrement(),
trigger.getBreachDuration().intValue(),
trigger.getStatus(), trigger.getCreatedTime().toGregorianCalendar());
ret.add(newTrigger);
}
return ret;
}
/**
* Creates a new auto scaling group
*
* @param autoScalingGroupName a autoScaling group name
* @param launchConfigurationName name of launch configuration for this group
* @param minSize min number of servers in this group
* @param maxSize max number of servers in this group (must be < 1000)
* @param cooldown number of seconds to wait before adjusting capacity
* @param availabilityZones zones for this group
* @throws AutoScalingException wraps checked exceptions
*/
public void createAutoScalingGroup(String autoScalingGroupName, String launchConfigurationName, int minSize, int maxSize,
int cooldown, List<String> availabilityZones) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("AutoScalingGroupName", autoScalingGroupName);
params.put("LaunchConfigurationName", launchConfigurationName);
params.put("MinSize", ""+minSize);
params.put("MaxSize", ""+maxSize);
params.put("Cooldown", ""+cooldown);
int i=0;
for (String zone : availabilityZones) {
params.put("AvailabilityZones.member."+(i+1), zone);
i++;
}
HttpGet method = new HttpGet();
// CreateAutoScalingGroupResponse response =
makeRequestInt(method, "CreateAutoScalingGroup", params, CreateAutoScalingGroupResponse.class);
}
/**
* Deletes a auto scaling group
*
* @param autoScalingGroupName a autoScaling group name
* @throws AutoScalingException wraps checked exceptions
*/
public void deleteAutoScalingGroup(String autoScalingGroupName) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("AutoScalingGroupName", autoScalingGroupName);
HttpGet method = new HttpGet();
// DeleteAutoScalingGroupResponse response =
makeRequestInt(method, "DeleteAutoScalingGroup", params, DeleteAutoScalingGroupResponse.class);
}
/**
* Describes one or more auto scaling groups
*
* @param autoScalingGroupNames a auto scaling group name
* @return autoScaling group descriptions
* @throws AutoScalingException wraps checked exceptions
*/
public List<AutoScalingGroup> describeAutoScalingGroups(List<String> autoScalingGroupNames) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
if (autoScalingGroupNames != null && autoScalingGroupNames.size() > 0) {
int i=0;
for (String name : autoScalingGroupNames) {
params.put("AutoScalingGroupNames.member."+(i+1), name);
i++;
}
}
HttpGet method = new HttpGet();
DescribeAutoScalingGroupsResponse response =
makeRequestInt(method, "DescribeAutoScalingGroups", params, DescribeAutoScalingGroupsResponse.class);
List<com.xerox.amazonws.typica.autoscale.jaxb.AutoScalingGroup> result =
response.getDescribeAutoScalingGroupsResult().getAutoScalingGroups().getMembers();
List<AutoScalingGroup> ret = new ArrayList<AutoScalingGroup>();
for (com.xerox.amazonws.typica.autoscale.jaxb.AutoScalingGroup group : result) {
List<String> zones = new ArrayList<String>();
for (String zone : group.getAvailabilityZones().getMembers()) {
zones.add(zone);
}
AutoScalingGroup newGroup = new AutoScalingGroup(group.getAutoScalingGroupName(),
group.getLaunchConfigurationName(), group.getMinSize().intValue(),
group.getMaxSize().intValue(),
group.getDesiredCapacity().intValue(),
group.getCooldown().intValue(),
zones, group.getCreatedTime().toGregorianCalendar());
List<com.xerox.amazonws.typica.autoscale.jaxb.Instance> instList = group.getInstances().getMembers();
for (com.xerox.amazonws.typica.autoscale.jaxb.Instance inst : instList) {
newGroup.addInstance(inst.getInstanceId(), inst.getLifecycleState());
}
ret.add(newGroup);
}
return ret;
}
/**
* Adjusts a auto scaling groups' capacity
*
* @param autoScalingGroupName a autoScaling group name
* @param desiredCapacity the new capacity setting
* @throws AutoScalingException wraps checked exceptions
*/
public void setDesiredCapacity(String autoScalingGroupName, int desiredCapacity) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("AutoScalingGroupName", autoScalingGroupName);
params.put("DesiredCapacity", ""+desiredCapacity);
HttpGet method = new HttpGet();
// SetDesiredCapacityResponse response =
makeRequestInt(method, "SetDesiredCapacity", params, SetDesiredCapacityResponse.class);
}
/**
* Update a auto scaling group
*
* @param autoScalingGroupName a autoScaling group name
* @param launchConfigurationName name of launch configuration for this group
* @param minSize min number of servers in this group
* @param maxSize max number of servers in this group (must be < 1000)
* @param defaultCooldown number of seconds to wait before adjusting capacity
* @throws AutoScalingException wraps checked exceptions
*/
public void updateAutoScalingGroup(String autoScalingGroupName, String launchConfigurationName, int minSize, int maxSize,
int defaultCooldown) throws AutoScalingException {
Map<String, String> params = new HashMap<String, String>();
params.put("AutoScalingGroupName", autoScalingGroupName);
params.put("LaunchConfigurationName", launchConfigurationName);
params.put("MinSize", ""+minSize);
params.put("MaxSize", ""+maxSize);
params.put("DefaultCooldown", ""+defaultCooldown);
HttpGet method = new HttpGet();
// UpdateAutoScalingGroupResponse response =
makeRequestInt(method, "UpdateAutoScalingGroup", params, UpdateAutoScalingGroupResponse.class);
}
protected <T> T makeRequestInt(HttpRequestBase method, String action, Map<String, String> params, Class<T> respType)
throws AutoScalingException {
try {
return makeRequest(method, action, params, respType);
} catch (AWSException ex) {
throw new AutoScalingException(ex);
} catch (JAXBException ex) {
throw new AutoScalingException("Problem parsing returned message.", ex);
} catch (SAXException ex) {
throw new AutoScalingException("Problem parsing returned message.", ex);
} catch (MalformedURLException ex) {
throw new AutoScalingException(ex.getMessage(), ex);
} catch (IOException ex) {
throw new AutoScalingException(ex.getMessage(), ex);
} catch (HttpException ex) {
throw new AutoScalingException(ex.getMessage(), ex);
}
}
}