def attach_volume(volume_id, device_path): set_region() ec2 = boto3.client("ec2") instance_id = InstanceInfo().instance_id() ec2.attach_volume(VolumeId=volume_id, InstanceId=instance_id, Device=device_path) wait_for_volume_status(volume_id, "attached")
def create_empty_volume(size_gb, availability_zone=None): set_region() ec2 = boto3.client("ec2") args = {'Size': size_gb, 'VolumeType': 'gp2'} if not availability_zone: availability_zone = InstanceInfo().availability_zone() args['AvailabilityZone'] = availability_zone resp = ec2.create_volume(**args) wait_for_volume_status(resp['VolumeId'], "available") return resp['VolumeId']
def delete_on_termination(device_path): set_region() ec2 = boto3.client("ec2") instance_id = InstanceInfo().instance_id() ec2.modify_instance_attribute(InstanceId=instance_id, BlockDeviceMappings=[{ "DeviceName": device_path, "Ebs": { "DeleteOnTermination": True } }])
def wait_for_volume_status(volume_id, status, timeout_sec=300): set_region() start = time.time() ec2 = boto3.client("ec2") volume = None while not match_volume_state(volume, status): time.sleep(2) if time.time() - start > timeout_sec: raise Exception("Failed waiting for status '" + status + "' for " + volume_id + " (timeout: " + str(timeout_sec) + ")") resp = ec2.describe_volumes(VolumeIds=[volume_id]) if "Volumes" in resp: volume = resp['Volumes'][0]
def wait_for_snapshot_complete(snapshot_id, timeout_sec=900): set_region() start = time.time() ec2 = boto3.client("ec2") snapshot = None while not is_snapshot_complete(snapshot): time.sleep(2) if time.time() - start > timeout_sec: raise Exception("Failed waiting for status 'completed' for " + snapshot_id + " (timeout: " + str(timeout_sec) + ")") resp = ec2.describe_snapshots(SnapshotIds=[snapshot_id]) if "Snapshots" in resp: snapshot = resp['Snapshots'][0]
def get_latest_snapshot(tag_name, tag_value): """Get the latest snapshot with a given tag """ set_region() ec2res = boto3.resource("ec2") snapshots = sorted(ec2res.snapshots.filter(Filters=[{ 'Name': 'tag:' + tag_name, 'Values': [tag_value] }]), key=lambda k: k.start_time, reverse=True) if snapshots: return snapshots[0] else: return None
def attached_devices(volume_id=None): set_region() ec2 = boto3.client("ec2") volumes = ec2.describe_volumes( Filters=[{ "Name": "attachment.instance-id", "Values": [InstanceInfo().instance_id()] }, { "Name": "attachment.status", "Values": ["attached"] }]) ret = [] for volume in volumes['Volumes']: for attachment in volume['Attachments']: if (not volume_id) or volume['VolumeId'] == volume_id: ret.append(attachment['Device']) return ret
def create_snapshot(tag_key, tag_value, mount_path, wait=False, tags={}, copytags=[]): set_region() create_tags = _create_tag_array(tag_key, tag_value, tags, copytags) device = device_from_mount_path(mount_path) with open(os.devnull, 'w') as devnull: subprocess.call(["sync", mount_path[0]], stdout=devnull, stderr=devnull) ec2 = boto3.client("ec2") volume_id = None if "/nvme" in device: proc = Popen(["nvme", "id-ctrl", device], stdout=PIPE, stderr=PIPE) out = proc.communicate()[0] for nvme_line in out.split("\n"): if nvme_line.startswith("sn"): volume_id = nvme_line.split()[2] if "vol-" not in volume_id: volume_id = volume_id.replace("vol", "vol-") break else: instance_id = InstanceInfo().instance_id() volume = ec2.describe_volumes(Filters=[{ "Name": "attachment.instance-id", "Values": [instance_id] }]) for volume in volume['Volumes']: if volume['Attachments'][0]['Device'] == device: volume_id = volume['VolumeId'] if volume_id: snap = ec2.create_snapshot(VolumeId=volume_id) ec2.create_tags(Resources=[snap['SnapshotId']], Tags=create_tags) else: raise Exception("Could not find volume for " + mount_path + "(" + device + ")") if wait: wait_for_snapshot_complete(snap['SnapshotId']) return snap['SnapshotId']
def clean_snapshots(days, tags): set_region() ec2 = boto3.client("ec2") account_id = resolve_account() newest_timestamp = datetime.utcnow() - timedelta(days=days) newest_timestamp = newest_timestamp.replace(tzinfo=None) paginator = ec2.get_paginator('describe_snapshots') for page in paginator.paginate(OwnerIds=[account_id], Filters=[{ 'Name': 'tag-value', 'Values': tags }], PaginationConfig={'PageSize': 1000}): for snapshot in page['Snapshots']: tags = {} for tag in snapshot['Tags']: tags[tag['Key']] = tag['Value'] print_time = snapshot['StartTime'].replace( tzinfo=tz.tzlocal()).timetuple() compare_time = snapshot['StartTime'].replace(tzinfo=None) if compare_time < newest_timestamp: print( colored("Deleting " + snapshot['SnapshotId'], "yellow") + " || " + time.strftime("%a, %d %b %Y %H:%M:%S", print_time) + " || " + json.dumps(tags)) try: ec2.delete_snapshot(SnapshotId=snapshot['SnapshotId']) time.sleep(0.2) except ClientError as err: print( colored( "Delete failed: " + err.response['Error']['Message'], "red")) else: print( colored("Skipping " + snapshot['SnapshotId'], "cyan") + " || " + time.strftime("%a, %d %b %Y %H:%M:%S", print_time) + " || " + json.dumps(tags))
def detach_volume(mount_path): set_region() device = device_from_mount_path(mount_path) if "/nvme" in device: proc = Popen(["nvme", "id-ctrl", device], stdout=PIPE, stderr=PIPE) out = proc.communicate()[0] for nvme_line in out.split("\n"): if nvme_line.startswith("sn"): volume_id = nvme_line.split()[2] if "vol-" not in volume_id: volume_id = volume_id.replace("vol", "vol-") break else: ec2 = boto3.client("ec2") instance_id = InstanceInfo().instance_id() volume = ec2.describe_volumes(Filters=[{ "Name": "attachment.device", "Values": [device] }, { "Name": "attachment.instance-id", "Values": [instance_id] }]) volume_id = volume['Volumes'][0]['VolumeId'] ec2.detach_volume(VolumeId=volume_id, InstanceId=instance_id)
def volume_from_snapshot(tag_key, tag_value, mount_path, availability_zone=None, size_gb=None, del_on_termination=True, tags=[], copytags=[]): set_region() snapshot = get_latest_snapshot(tag_key, tag_value) if snapshot: print("Found snapshot " + snapshot.id) volume = create_volume(snapshot.id, availability_zone=availability_zone, size_gb=size_gb) else: if not size_gb: size_gb = 32 print("Creating empty volyme of size " + str(size_gb)) volume = create_empty_volume(size_gb, availability_zone=availability_zone) tag_volume(volume, tag_key, tag_value, tags, copytags) device = first_free_device() print("Attaching volume " + volume + " to " + device) attach_volume(volume, device) local_device = map_local_device(volume, device) if del_on_termination: delete_on_termination(device) if not snapshot: # empty device if sys.platform.startswith('win'): # Windows format drive_letter = mount_path[0].upper() disk = wmic_disk_with_target_id(letter_to_target_id(device[-1:])) if not disk: disk = wmic_disk_with_volume_id(volume) disk_number = str(disk['Index']) subprocess.check_call([ "powershell.exe", "Get-Disk", disk_number, "|", "Set-Disk", "-IsOffline", "$False" ]) subprocess.check_call([ "powershell.exe", "Initialize-Disk", disk_number, "-PartitionStyle", "MBR" ]) subprocess.check_call([ "powershell.exe", "New-Partition", "-DiskNumber", disk_number, "-UseMaximumSize", "-DriveLetter", drive_letter ]) print("Formatting " + device + "(" + drive_letter + ":)") subprocess.check_call([ "powershell.exe", "Format-Volume", "-DriveLetter", drive_letter, "-FileSystem", "NTFS", "-Force", "-Confirm:$False" ]) else: # linux format print("Formatting " + local_device) subprocess.check_call(["mkfs.ext4", local_device]) else: if sys.platform.startswith('win'): target_id = letter_to_target_id(device[-1:]) drive_letter = mount_path[0].upper() disk = wmic_disk_with_target_id(target_id) if not disk: disk = wmic_disk_with_volume_id(volume) disk_number = str(disk['Index']) with open(os.devnull, 'w') as devnull: subprocess.call([ "powershell.exe", "Initialize-Disk", disk_number, "-PartitionStyle", "MBR" ], stderr=devnull, stdout=devnull) subprocess.check_call([ "powershell.exe", "Get-Disk", disk_number, "|", "Set-Disk", "-IsOffline", "$False" ]) with open(os.devnull, 'w') as devnull: subprocess.check_call([ "powershell.exe", "Get-Partition", "-DiskNumber", disk_number, "-PartitionNumber", "1" "|", "Set-Partition", "-NewDriveLetter", drive_letter ], stdout=devnull, stderr=devnull) # resize win partition if necessary if size_gb and not size_gb == snapshot.volume_size: proc = subprocess.Popen([ "powershell.exe", "$((Get-PartitionSupportedSize -Dri" + "veLetter " + drive_letter + ").SizeMax)" ], stdout=subprocess.PIPE) max_size = proc.communicate()[0] subprocess.check_call([ "powershell.exe", "Resize-Partition", "-DriveLetter", drive_letter, "-Size", max_size ]) else: if size_gb and not size_gb == snapshot.volume_size: print("Resizing " + local_device + " from " + str(snapshot.volume_size) + "GB to " + str(size_gb)) try: subprocess.check_call(["e2fsck", "-f", "-p", local_device]) except CalledProcessError as e: print("Filesystem check returned " + str(e.returncode)) if e.returncode > 1: raise Exception( "Uncorrected filesystem errors - please fix manually" ) subprocess.check_call(["resize2fs", local_device]) if not sys.platform.startswith('win'): if not os.path.isdir(mount_path): os.makedirs(mount_path) subprocess.check_call(["mount", local_device, mount_path])