def create_efs(config): """Create and configure EFS Args: config (aws_ml_helper.config.Config): Configuration """ efs = boto.client('efs', config) print('Creating EFS') token = f'{config.vpc_name}-efs' response = efs.create_file_system(CreationToken=token) efs_id = response['FileSystemId'] # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) efs.create_tags(FileSystemId=efs_id, Tags=[{ 'Key': 'Name', 'Value': token }]) # Wait until it's in the available state while True: response = efs.describe_file_systems(FileSystemId=efs_id) if response['FileSystems'][0]['LifeCycleState'] == 'available': break efs.create_mount_target(FileSystemId=efs_id, SubnetId=config.subnet_id, SecurityGroups=[config.efs_security_group_id]) config.efs_id = efs_id config.save()
def generate_key_pair(config, path): """Generate key pair and save it to the keys subdirectory Args: config (aws_ml_helper.config.Config): Configuration path (str): Path to the directory where the access key should be saved """ ec2 = boto.client('ec2', config) print('Generating key-pair') full_name = f'access-key-{config.vpc_name}' response = ec2.create_key_pair(KeyName=full_name) if not os.path.isdir(path): os.makedirs(path) key_path = os.path.join(path, f'{full_name}.pem') with io.open(key_path, 'w') as f: f.write(response['KeyMaterial']) os.chmod(key_path, 0o400) config.access_key = key_path config.save()
def spot_price(config, days=7, instance_type=None, value='all'): """Show information about spot instance prices in the last n days Args: config (aws_ml_helper.config.Config): Configuration days (int): Show information for the last n days instance_type (str): Select instance type. If not provided function will use the value in the configuration. value (str): Pick which value to show. Default all. """ end_time = datetime.now() start_time = end_time - timedelta(days=days) ec2 = boto.client('ec2', config) r = ec2.describe_spot_price_history( StartTime=start_time, EndTime=end_time, InstanceTypes=[instance_type or config.instance_type], ) prices = [float(p['SpotPrice']) for p in r['SpotPriceHistory']] if value == 'all': print(tabulate([ ['Min', min(prices)], ['Max', max(prices)], ['Mean', sum(prices) / len(prices)], ['Median', median(prices)] ], tablefmt=config.table_format, floatfmt='.3f')) elif value == 'min': print(min(prices)) elif value == 'max': print(max(prices)) elif value == 'mean': print(sum(prices) / len(prices)) elif value == 'median': print(median(prices))
def start_spot_instance(config, name, bid_price, ami_id=None, instance_type=None, snapshot_name=None, mount_point=None): """Starts a spot instance. Args: config (aws_ml_helper.config.Config): Configuration name (str): Spot instance name bid_price (int): Bidding price for the instance ami_id (str): AMI id to use. If not provided, value form the configuration will be used instance_type (str): Instance type to use. If not provided, value from the configuration will be used. snapshot_name (str): Name of the snapshot from which the attached volume will be created mount_point (str): Path where the volume should be mounted """ ec2 = boto.client('ec2', config) response = ec2.request_spot_instances( InstanceCount=1, Type='one-time', LaunchSpecification={ 'ImageId': ami_id or config.ami_id, 'InstanceType': instance_type or config.instance_type, 'KeyName': f'access-key-{config.vpc_name}', 'EbsOptimized': True, 'Placement': { 'AvailabilityZone': config.availability_zone, }, # 'BlockDeviceMappings': [ # { # 'DeviceName': '/dev/sda1', # 'Ebs': { # 'DeleteOnTermination': False, # 'VolumeSize': 128, # 'VolumeType': 'gp2' # }, # }, # ], 'Monitoring': {'Enabled': True}, 'NetworkInterfaces': [ { 'DeviceIndex': 0, 'AssociatePublicIpAddress': True, 'Groups': [config.ec2_security_group_id], 'SubnetId': config.subnet_id }, ], }, SpotPrice=f'{bid_price}', InstanceInterruptionBehavior='terminate' ) click.echo('Spot instance request created.') request_id = response['SpotInstanceRequests'][0]['SpotInstanceRequestId'] waiter = ec2.get_waiter('spot_instance_request_fulfilled') waiter.wait(SpotInstanceRequestIds=[request_id]) response = ec2.describe_spot_instance_requests( SpotInstanceRequestIds=[request_id] ) instance_id = response['SpotInstanceRequests'][0]['InstanceId'] waiter = ec2.get_waiter('instance_running') waiter.wait(InstanceIds=[instance_id]) click.echo(f'Spot Instance ID: {instance_id}') ec2.create_tags(Resources=[instance_id], Tags=[{'Key': 'Name', 'Value': name}]) response = ec2.describe_instances( InstanceIds=[instance_id], Filters=[{'Name': 'instance-state-name', 'Values': ['running']}] ) instance_ip = ( response['Reservations'][0]['Instances'][0]['PublicIpAddress'] ) click.echo(f'Spot Instance IP: {instance_ip}') mount_point = mount_point or config.mount_point if mount_point in ('', None): # Mount point is not defined we don't know where to mount the volume return ec2 = boto.resource('ec2', config) # Search for a volume with the same name volume = get_volume(config, name) if volume is not None: click.echo(f'Volume "{name}" found - attaching') # Attach the volume volume_attach(config, name, name, device='xvdh') run(config, name, f'sudo mount /dev/xvdh {mount_point}') else: if snapshot_name is not None: snapshot = get_snapshot(config, snapshot_name) elif config.snapshot_id not in ('', None): snapshot = ec2.Snapshot(config.snapshot_id) snapshot_name = name_from_tags(snapshot.tags) else: # Snapshot not found return return click.echo(f'Creating volume "{name}" from snapshot "{snapshot_name}"') volume_create(config, name, size=snapshot.volume_size, snapshot_name=snapshot_name, wait=True) click.echo(f'Attaching volume "{name}"') volume_attach(config, name, name, device='xvdh') run(config, name, f'sudo mount /dev/xvdh {mount_point}')
def create_vpc(config, name, network_range='10.0.0.0/16', allowed_ip='0.0.0.0/32', availability_zone='us-east-1a'): """Creates a VPC Args: config (aws_ml_helper.config.Config): Configuration object name (str): VPC name network_range (str): The IPv4 network range for the VPC, in CIDR notation. For example, 10.0.0.0/16 allowed_ip (str): Public IP address from which the VPC instances will be accessible in CIDR notation. availability_zone: VPC availability zone """ ec2 = boto.client('ec2', config) print('Create VPC') response = ec2.create_vpc(CidrBlock=network_range) vpc_id = response['Vpc']['VpcId'] # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) ec2.create_tags(Resources=[vpc_id], Tags=[{'Key': 'Name', 'Value': name}]) ec2.modify_vpc_attribute(VpcId=vpc_id, EnableDnsHostnames={'Value': True}) ec2.modify_vpc_attribute(VpcId=vpc_id, EnableDnsSupport={'Value': True}) print('Create the gateway') response = ec2.create_internet_gateway() # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) gateway_id = response['InternetGateway']['InternetGatewayId'] ec2.create_tags(Resources=[gateway_id], Tags=[{ 'Key': 'Name', 'Value': f'{name}-gateway' }]) ec2.attach_internet_gateway(VpcId=vpc_id, InternetGatewayId=gateway_id) print('Create subnet') response = ec2.create_subnet(VpcId=vpc_id, CidrBlock=network_range, AvailabilityZone=availability_zone) # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) subnet_id = response['Subnet']['SubnetId'] ec2.create_tags(Resources=[subnet_id], Tags=[{ 'Key': 'Name', 'Value': f'{name}-subnet' }]) print('Create routing table') response = ec2.create_route_table(VpcId=vpc_id) # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) route_table_id = response['RouteTable']['RouteTableId'] ec2.create_tags(Resources=[route_table_id], Tags=[{ 'Key': 'Name', 'Value': f'{name}-route-table' }]) ec2.associate_route_table(RouteTableId=route_table_id, SubnetId=subnet_id) ec2.create_route(RouteTableId=route_table_id, GatewayId=gateway_id, DestinationCidrBlock='0.0.0.0/0') print('Create security group for instances') response = ec2.create_security_group( VpcId=vpc_id, GroupName=f'{name}-ec2-security-group', Description=f'Security Group for {name} VPC instances') # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) ec2_security_group_id = response['GroupId'] # Open port for ssh ec2.authorize_security_group_ingress(GroupId=ec2_security_group_id, IpProtocol='tcp', FromPort=22, ToPort=22, CidrIp=allowed_ip) # Open port for tensorboard ec2.authorize_security_group_ingress(GroupId=ec2_security_group_id, IpProtocol='tcp', FromPort=6006, ToPort=6006, CidrIp=allowed_ip) # Open port for jupyter notebook ec2.authorize_security_group_ingress(GroupId=ec2_security_group_id, IpProtocol='tcp', FromPort=8888, ToPort=8888, CidrIp=allowed_ip) print('Create security group for EFS') response = ec2.create_security_group( VpcId=vpc_id, GroupName=f'{name}-efs-security-group', Description=f'Security Group for {name} VPC EFS') # Sleep for a second because the the object is created asynchronously. It's # not created when the response comes back from the server. time.sleep(1) efs_security_group_id = response['GroupId'] ec2.authorize_security_group_ingress(GroupId=efs_security_group_id, IpPermissions=[ { 'FromPort': 2049, 'IpProtocol': 'tcp', 'ToPort': 2049, 'UserIdGroupPairs': [{ 'GroupId': ec2_security_group_id }] }, ]) config.vpc_id = vpc_id config.vpc_name = name config.subnet_id = subnet_id config.ec2_security_group_id = ec2_security_group_id config.efs_security_group_id = efs_security_group_id config.save() return { 'vpc_id': vpc_id, 'vpc_name': name, 'subnet': subnet_id, 'ec2_security_group': ec2_security_group_id, 'efs_security_group': efs_security_group_id }