Source code for launch

"""
Handle the instance:
* Launch new instance with spot request
* Terminate instance
* Check instance connectivity
"""

import socket
import sys
import time
from errno import ECONNREFUSED
from errno import ETIMEDOUT


[docs]def query_yes_no(question, default="yes"): """Ask a yes/no question via raw_input() and return their answer. :param question: is a string that is presented to the user. :param default: is the presumed answer if the user just hits <Enter>. It must be "yes" (the default), "no" or None (meaning an answer is required of the user). :return: The "answer" return value is True for "yes" or False for "no". """ valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} if default is None: prompt = " [y/n] " elif default == "yes": prompt = " [Y/n] " elif default == "no": prompt = " [y/N] " else: raise ValueError("invalid default answer: '%s'" % default) while True: sys.stdout.write(question + prompt) choice = input().lower() if default is not None and choice == '': return valid[default] elif choice in valid: return valid[choice] else: sys.stdout.write("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n")
[docs]def ping(host, port): """ Ping the port of the host :param host: the host to ping :param port: the port of the host to ping :return: the port if the port is open or False if there is a connection error """ try: socket.socket().connect((host, port)) print(str(port) + " Open") return port except socket.error as err: if err.errno == ECONNREFUSED or err.errno == ETIMEDOUT: return False raise
[docs]def wait_ping(client, instance_ids, pending_instance_ids): """Wait that all the instance have open the ssh port (22) :param client: the ec2 client :param instance_ids: id of all the instance to ping :param pending_instance_ids: id of the remaining instance to ping :return: Exit when all the instance are reachable on port 22 """ results = client.describe_instances(InstanceIds=pending_instance_ids) for result in results["Reservations"]: for instance in result["Instances"]: if ping(instance["PublicDnsName"], 22) == 22: pending_instance_ids.pop(pending_instance_ids.index(instance["InstanceId"])) print("instance `{}` ping ok!".format(instance["InstanceId"])) else: print("pinging on `{}`".format(instance["InstanceId"])) if len(pending_instance_ids) == 0: print("all instances running!") else: time.sleep(2) wait_ping(client, instance_ids, pending_instance_ids)
[docs]def wait_for_running(client, instance_ids, pending_instance_ids): """Wait that all the instance are in running state :param client: the ec2 client :param instance_ids: id of all the instance to check running :param pending_instance_ids: id of the remaining instance to check running :return: Exit when all the instance are running """ results = client.describe_instances(InstanceIds=pending_instance_ids) for result in results["Reservations"]: for instance in result["Instances"]: if instance["State"]["Name"] == 'running': pending_instance_ids.pop(pending_instance_ids.index(instance["InstanceId"])) print("instance `{}` running!".format(instance["InstanceId"])) else: print("waiting on `{}`".format(instance["InstanceId"])) if len(pending_instance_ids) == 0: print("all instances running!") else: time.sleep(10) wait_for_running(client, instance_ids, pending_instance_ids)
[docs]def wait_for_fulfillment(client, request_ids, pending_request_ids): """Wait that all the spot instance request are fulfilled :param client: the ec2 client :param request_ids: id of all the spot instance request to fulfill :param pending_request_ids: id of the remaining spot instance request to fulfill :return: Exit when all the spot request are fulfilled """ results = client.describe_spot_instance_requests(SpotInstanceRequestIds=pending_request_ids) for result in results["SpotInstanceRequests"]: if result["Status"]["Code"] == 'fulfilled': pending_request_ids.pop(pending_request_ids.index(result["SpotInstanceRequestId"])) print("spot request `{}` fulfilled!".format(result["SpotInstanceRequestId"])) else: print("waiting on `{}`".format(result["SpotInstanceRequestId"])) if len(pending_request_ids) == 0: print("all spots fulfilled!") else: time.sleep(10) wait_for_fulfillment(client, request_ids, pending_request_ids)
[docs]def terminate(client, spot_request_ids, instance_ids): """Delete all the spot_request_ids and terminate al the instance_ids :param client: the ec2 client :param spot_request_ids: id of all the spot instance request to delete :param instance_ids: id of all the instance to terminate :return: """ # DELETE SPOT REQUEST client.cancel_spot_instance_requests(SpotInstanceRequestIds=spot_request_ids) # TERMINATE INSTANCE client.instances.filter(InstanceIds=instance_ids).stop() client.instances.filter(InstanceIds=instance_ids).terminate()
[docs]def check_spot_price(client, config): """Check the current spot price on the selected amazon region of the instance type choosen and compare with the one provided by the user :param client: the ec2 client :param config: the configuration dictionary of the user :return: Exit if the spot price of the user is too low (< current price + 20%) """ spot_price_history_response = client.describe_spot_price_history( InstanceTypes=[config["Aws"]["InstanceType"]], ProductDescriptions=['Linux/UNIX'], AvailabilityZone=config["Aws"]["AZ"]) print(spot_price_history_response['SpotPriceHistory'][0]) last_spot_price = [float(x['SpotPrice']) for x in spot_price_history_response['SpotPriceHistory'][:10]] print(last_spot_price) print("Number of responses:", len(spot_price_history_response['SpotPriceHistory'])) spot_price = float(sum(last_spot_price)) / max(len(last_spot_price), 1) spot_price += (spot_price * 0.2) spot_price = float("{0:.2f}".format(spot_price)) print("LAST 10 SPOT PRICE + 20%: " + str(spot_price)) print("YOUR PRICE: " + str(config["Aws"]["Price"])) if float(config["Aws"]["Price"]) < spot_price: print("ERROR PRICE") exit(1)
[docs]def launch(client, num_instance, config): """ Launch num_instance on Amazon EC2 with spot request :param client: the ec2 client :param num_instance: number of instance to launch :param config: the configuration dictionary of the user :return: the list of spot request's ids """ if query_yes_no("Are you sure to launch " + str(num_instance) + " new instance?", "no"): check_spot_price(client, config) spot_request_response = client.request_spot_instances( SpotPrice=config["Aws"]["Price"], InstanceCount=num_instance, Type='one-time', AvailabilityZoneGroup= config["Aws"]["AZ"], LaunchSpecification={ "ImageId": config["Aws"]["AMI"], "KeyName": config["Aws"]["KeyPair"], "SecurityGroups": [ config["Aws"]["SecurityGroup"], ], 'Placement': { 'AvailabilityZone': config["Aws"]["AZ"], }, "InstanceType": config["Aws"]["InstanceType"], "EbsOptimized": config["Aws"]["EbsOptimized"], "BlockDeviceMappings": [ { "DeviceName": "/dev/sda1", "Ebs": { "DeleteOnTermination": True, "VolumeType": "gp2", "VolumeSize": 200, "SnapshotId": config["Aws"]["SnapshotId"] } }, { "DeviceName": "/dev/sdb", "VirtualName": "ephemeral0" } ], }) print([req["SpotInstanceRequestId"] for req in spot_request_response["SpotInstanceRequests"]]) return [req["SpotInstanceRequestId"] for req in spot_request_response["SpotInstanceRequests"]]