/* * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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. */ import java.util.ArrayList; import java.util.Date; import java.util.List; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.BlockDeviceMapping; import com.amazonaws.services.ec2.model.CancelSpotInstanceRequestsRequest; import com.amazonaws.services.ec2.model.CreateTagsRequest; import com.amazonaws.services.ec2.model.DescribeSpotInstanceRequestsRequest; import com.amazonaws.services.ec2.model.DescribeSpotInstanceRequestsResult; import com.amazonaws.services.ec2.model.EbsBlockDevice; import com.amazonaws.services.ec2.model.Instance; import com.amazonaws.services.ec2.model.LaunchSpecification; import com.amazonaws.services.ec2.model.RequestSpotInstancesRequest; import com.amazonaws.services.ec2.model.RequestSpotInstancesResult; import com.amazonaws.services.ec2.model.RunInstancesRequest; import com.amazonaws.services.ec2.model.RunInstancesResult; import com.amazonaws.services.ec2.model.SpotInstanceRequest; import com.amazonaws.services.ec2.model.SpotPlacement; import com.amazonaws.services.ec2.model.Tag; import com.amazonaws.services.ec2.model.TerminateInstancesRequest; public class Requests { private AmazonEC2 ec2; private ArrayList<String> instanceIds; private ArrayList<String> spotInstanceRequestIds; private String instanceType; private String amiID; private String bidPrice; private String securityGroup; private String placementGroupName; private boolean deleteOnTermination; private String availabilityZoneName; private String availabilityZoneGroupName; private String launchGroupName; private Date validFrom; private Date validTo; private String requestType; /** * Public constructor. * @throws Exception */ public Requests (String instanceType, String amiID, String bidPrice, String securityGroup) throws Exception { init(instanceType, amiID, bidPrice,securityGroup); } /** * The only information needed to create a client are security credentials * consisting of the AWS Access Key ID and Secret Access Key. All other * configuration, such as the service endpoints, are performed * automatically. Client parameters, such as proxies, can be specified in an * optional ClientConfiguration object when constructing a client. * * @see com.amazonaws.auth.BasicAWSCredentials * @see com.amazonaws.auth.PropertiesCredentials * @see com.amazonaws.ClientConfiguration */ private void init(String instanceType, String amiID, String bidPrice, String securityGroup) throws Exception { /* * The ProfileCredentialsProvider will return your [default] * credential profile by reading from the credentials file located at * (~/.aws/credentials). */ AWSCredentials credentials = null; try { credentials = new ProfileCredentialsProvider().getCredentials(); } catch (Exception e) { throw new AmazonClientException( "Cannot load the credentials from the credential profiles file. " + "Please make sure that your credentials file is at the correct " + "location (~/.aws/credentials), and is in valid format.", e); } ec2 = new AmazonEC2Client(credentials); Region usWest2 = Region.getRegion(Regions.US_WEST_2); ec2.setRegion(usWest2); this.instanceType = instanceType; this.amiID = amiID; this.bidPrice = bidPrice; this.securityGroup = securityGroup; this.deleteOnTermination = true; this.placementGroupName = null; } /** * The submit method will create 1 x one-time t1.micro request with a maximum bid * price of $0.03 using the Amazon Linux AMI. * * Note the AMI id may change after the release of this code sample, and it is important * to use the latest. You can find the latest version by logging into the AWS Management * console, and attempting to perform a launch. You will be presented with AMI options, * one of which will be Amazon Linux. Simply use that AMI id. */ public void submitRequests() { //==========================================================================// //================= Submit a Spot Instance Request =====================// //==========================================================================// // Initializes a Spot Instance Request RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest(); // Request 1 x t1.micro instance with a bid price of $0.03. requestRequest.setSpotPrice(bidPrice); requestRequest.setInstanceCount(Integer.valueOf(1)); // Setup the specifications of the launch. This includes the instance type (e.g. t1.micro) // and the latest Amazon Linux AMI id available. Note, you should always use the latest // Amazon Linux AMI id or another of your choosing. LaunchSpecification launchSpecification = new LaunchSpecification(); launchSpecification.setImageId(amiID); launchSpecification.setInstanceType(instanceType); // Add the security group to the request. ArrayList<String> securityGroups = new ArrayList<String>(); securityGroups.add(securityGroup); launchSpecification.setSecurityGroups(securityGroups); // If a placement group has been set, then we will use it in the request. if (placementGroupName != null && !placementGroupName.equals("")) { // Setup the placement group to use with whatever name you desire. SpotPlacement placement = new SpotPlacement(); placement.setGroupName(placementGroupName); launchSpecification.setPlacement(placement); } // Check to see if we need to set the availability zone name. if (availabilityZoneName != null && !availabilityZoneName.equals("")) { // Setup the availability zone to use. Note we could retrieve the availability // zones using the ec2.describeAvailabilityZones() API. SpotPlacement placement = new SpotPlacement(availabilityZoneName); launchSpecification.setPlacement(placement); } if (availabilityZoneGroupName != null && !availabilityZoneGroupName.equals("")) { // Set the availability zone group. requestRequest.setAvailabilityZoneGroup(availabilityZoneGroupName); } // Check to see if we need to set the launch group. if (launchGroupName != null && !launchGroupName.equals("")) { // Set the availability launch group. requestRequest.setLaunchGroup(launchGroupName); } // Check to see if we need to set the valid from option. if (validFrom != null) { requestRequest.setValidFrom(validFrom); } // Check to see if we need to set the valid until option. if (validTo != null) { requestRequest.setValidUntil(validFrom); } // Check to see if we need to set the request type. if (requestType != null && !requestType.equals("")) { // Set the type of the bid. requestRequest.setType(requestType); } // If we should delete the EBS boot partition on termination. if (!deleteOnTermination) { // Create the block device mapping to describe the root partition. BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping(); blockDeviceMapping.setDeviceName("/dev/sda1"); // Set the delete on termination flag to false. EbsBlockDevice ebs = new EbsBlockDevice(); ebs.setDeleteOnTermination(Boolean.FALSE); blockDeviceMapping.setEbs(ebs); // Add the block device mapping to the block list. ArrayList<BlockDeviceMapping> blockList = new ArrayList<BlockDeviceMapping>(); blockList.add(blockDeviceMapping); // Set the block device mapping configuration in the launch specifications. launchSpecification.setBlockDeviceMappings(blockList); } // Add the launch specifications to the request. requestRequest.setLaunchSpecification(launchSpecification); // Call the RequestSpotInstance API. RequestSpotInstancesResult requestResult = ec2.requestSpotInstances(requestRequest); List<SpotInstanceRequest> requestResponses = requestResult.getSpotInstanceRequests(); // Setup an arraylist to collect all of the request ids we want to watch hit the running // state. spotInstanceRequestIds = new ArrayList<String>(); // Add all of the request ids to the hashset, so we can determine when they hit the // active state. for (SpotInstanceRequest requestResponse : requestResponses) { System.out.println("Created Spot Request: "+requestResponse.getSpotInstanceRequestId()); spotInstanceRequestIds.add(requestResponse.getSpotInstanceRequestId()); } } public void launchOnDemand () { //============================================================================================// //====================================== Launch an On-Demand Instance ========================// //====================================== If we Didn't Get a Spot Instance ====================// //============================================================================================// // Setup the request for 1 x t1.micro using the same security group and // AMI id as the Spot request. RunInstancesRequest runInstancesRequest = new RunInstancesRequest(); runInstancesRequest.setInstanceType(instanceType); runInstancesRequest.setImageId(amiID); runInstancesRequest.setMinCount(Integer.valueOf(1)); runInstancesRequest.setMaxCount(Integer.valueOf(1)); // Add the security group to the request. ArrayList<String> securityGroups = new ArrayList<String>(); securityGroups.add(securityGroup); runInstancesRequest.setSecurityGroups(securityGroups); // Launch the instance. RunInstancesResult runResult = ec2.runInstances(runInstancesRequest); // Add the instance id into the instance id list, so we can potentially later // terminate that list. for (Instance instance: runResult.getReservation().getInstances()) { System.out.println("Launched On-Demand Instace: "+instance.getInstanceId()); instanceIds.add(instance.getInstanceId()); } } /** * The areOpen method will determine if any of the requests that were started are still * in the open state. If all of them have transitioned to either active, cancelled, or * closed, then this will return false. * @return */ public boolean areAnyOpen() { //==========================================================================// //============== Describe Spot Instance Requests to determine =============// //==========================================================================// // Create the describeRequest with tall of the request id to monitor (e.g. that we started). DescribeSpotInstanceRequestsRequest describeRequest = new DescribeSpotInstanceRequestsRequest(); describeRequest.setSpotInstanceRequestIds(spotInstanceRequestIds); System.out.println("Checking to determine if Spot Bids have reached the active state..."); // Initialize variables. instanceIds = new ArrayList<String>(); try { // Retrieve all of the requests we want to monitor. DescribeSpotInstanceRequestsResult describeResult = ec2.describeSpotInstanceRequests(describeRequest); List<SpotInstanceRequest> describeResponses = describeResult.getSpotInstanceRequests(); // Look through each request and determine if they are all in the active state. for (SpotInstanceRequest describeResponse : describeResponses) { System.out.println(" " +describeResponse.getSpotInstanceRequestId() + " is in the "+describeResponse.getState() + " state."); // If the state is open, it hasn't changed since we attempted to request it. // There is the potential for it to transition almost immediately to closed or // cancelled so we compare against open instead of active. if (describeResponse.getState().equals("open")) { return true; } // Add the instance id to the list we will eventually terminate. instanceIds.add(describeResponse.getInstanceId()); } } catch (AmazonServiceException e) { // Print out the error. System.out.println("Error when calling describeSpotInstances"); System.out.println("Caught Exception: " + e.getMessage()); System.out.println("Reponse Status Code: " + e.getStatusCode()); System.out.println("Error Code: " + e.getErrorCode()); System.out.println("Request ID: " + e.getRequestId()); // If we have an exception, ensure we don't break out of the loop. // This prevents the scenario where there was blip on the wire. return true; } return false; } /** * Tag any of the resources we specify. * @param resources * @param tags */ private void tagResources(List<String> resources, List<Tag> tags) { // Create a tag request. CreateTagsRequest createTagsRequest = new CreateTagsRequest(); createTagsRequest.setResources(resources); createTagsRequest.setTags(tags); // Try to tag the Spot request submitted. try { ec2.createTags(createTagsRequest); } catch (AmazonServiceException e) { // Write out any exceptions that may have occurred. System.out.println("Error terminating instances"); System.out.println("Caught Exception: " + e.getMessage()); System.out.println("Reponse Status Code: " + e.getStatusCode()); System.out.println("Error Code: " + e.getErrorCode()); System.out.println("Request ID: " + e.getRequestId()); } } /** * Tags all of the instances started with this object with the tags specified. * @param tags */ public void tagInstances(List<Tag> tags) { tagResources(instanceIds, tags); } /** * Tags all of the requests started with this object with the tags specified. * @param tags */ public void tagRequests(List<Tag> tags) { tagResources(spotInstanceRequestIds, tags); } /** * The cleanup method will cancel and active requests and terminate any running instances * that were created using this object. */ public void cleanup () { //==========================================================================// //================= Cancel/Terminate Your Spot Request =====================// //==========================================================================// try { // Cancel requests. System.out.println("Cancelling requests."); CancelSpotInstanceRequestsRequest cancelRequest = new CancelSpotInstanceRequestsRequest(spotInstanceRequestIds); ec2.cancelSpotInstanceRequests(cancelRequest); } catch (AmazonServiceException e) { // Write out any exceptions that may have occurred. System.out.println("Error cancelling instances"); System.out.println("Caught Exception: " + e.getMessage()); System.out.println("Reponse Status Code: " + e.getStatusCode()); System.out.println("Error Code: " + e.getErrorCode()); System.out.println("Request ID: " + e.getRequestId()); } try { // Terminate instances. System.out.println("Terminate instances"); TerminateInstancesRequest terminateRequest = new TerminateInstancesRequest(instanceIds); ec2.terminateInstances(terminateRequest); } catch (AmazonServiceException e) { // Write out any exceptions that may have occurred. System.out.println("Error terminating instances"); System.out.println("Caught Exception: " + e.getMessage()); System.out.println("Reponse Status Code: " + e.getStatusCode()); System.out.println("Error Code: " + e.getErrorCode()); System.out.println("Request ID: " + e.getRequestId()); } // Delete all requests and instances that we have terminated. instanceIds.clear(); spotInstanceRequestIds.clear(); } /** * Sets the request type to either persistent or one-time. */ public void setRequestType(String type) { this.requestType = type; } /** * Sets the valid to and from time. If you set either value to null * or "" then the period will not be set. * @param from * @param to */ public void setValidPeriod(Date from, Date to) { this.validFrom = from; this.validTo = to; } /** * Sets the launch group to be used. If you set this to null * or "" then launch group will be used. * @param az */ public void setLaunchGroup(String launchGroup) { this.launchGroupName = launchGroup; } /** * Sets the availability zone group to be used. If you set this to null * or "" then availability zone group will be used. * @param az */ public void setAvailabilityZoneGroup(String azGroup) { this.availabilityZoneGroupName = azGroup; } /** * Sets the availability zone to be used. If you set this to null * or "" then availability zone will be used. * @param az */ public void setAvailabilityZone(String az) { this.availabilityZoneName = az; } /** * Sets the placementGroupName to be used. If you set this to null * or "" then no placementgroup will be used. * @param pg */ public void setPlacementGroup(String pg) { this.placementGroupName = pg; } /** * This sets the deleteOnTermination flag, so that we can determine whether or not * we should delete the root partition if the instance is interrupted or terminated. * @param terminate */ public void setDeleteOnTermination(boolean terminate) { this.deleteOnTermination = terminate; } }