Skip to content
This repository has been archived by the owner on Jun 10, 2019. It is now read-only.

NVMe support for ec2 provider #486

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 55 additions & 7 deletions bootstrapvz/providers/ec2/ebsvolume.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from bootstrapvz.base.fs.volume import Volume
from bootstrapvz.base.fs.exceptions import VolumeError
from bootstrapvz.common.tools import log_check_call


class EBSVolume(Volume):
Expand Down Expand Up @@ -35,17 +36,47 @@ def attach(self, instance_id):
def _before_attach(self, e):
import os.path
import string
import urllib2

def name_mapped(path):
return path.split('/')[-1].replace('xvd', 'sd')[:3]

self.instance_id = e.instance_id
for letter in string.ascii_lowercase[5:]:
dev_path = os.path.join('/dev', 'xvd' + letter)
if not os.path.exists(dev_path):
self.device_path = dev_path
self.ec2_device_path = os.path.join('/dev', 'sd' + letter)

dev_map_names = set()
launch_map_url = 'http://169.254.169.254/latest/meta-data/block-device-mapping/'
launch_map_response = urllib2.urlopen(url=launch_map_url, timeout=5)
for map_name in [d.strip() for d in launch_map_response.readlines()]:
dev_url = launch_map_url + map_name
dev_response = urllib2.urlopen(url=dev_url, timeout=5)
dev_map_names.add(name_mapped(dev_response.read().strip()))

try:
instance = self.conn.describe_instances(
Filters=[
{'Name': 'instance-id', 'Values': [self.instance_id]}
]
)['Reservations'][0]['Instances'][0]
except (IndexError, KeyError):
raise VolumeError('Unable to fetch EC2 instance volume data')

for mapped_dev in instance.get('BlockDeviceMappings', list()):
dev_map_names.add(name_mapped(mapped_dev['DeviceName']))

for letter in reversed(string.ascii_lowercase[1:]):
if 'sd' + letter not in dev_map_names:
self.ec2_device_path = '/dev/sd' + letter
break

if self.device_path is None:
raise VolumeError('Unable to find a free block device path for mounting the bootstrap volume')
if self.ec2_device_path is None:
raise VolumeError('Unable to find a free block device mapping for bootstrap volume')

self.device_path = None

lsblk_command = ['lsblk', '--noheadings', '--list', '--nodeps', '--output', 'NAME']

lsblk_start = log_check_call(lsblk_command)
start_dev_names = set(lsblk_start)

self.conn.attach_volume(VolumeId=self.vol_id,
InstanceId=self.instance_id,
Expand All @@ -54,6 +85,23 @@ def _before_attach(self, e):
waiter.wait(VolumeIds=[self.vol_id],
Filters=[{'Name': 'attachment.status', 'Values': ['attached']}])

log_check_call(['udevadm', 'settle'])

lsblk_end = log_check_call(lsblk_command)
end_dev_names = set(lsblk_end)

if len(start_dev_names ^ end_dev_names) != 1:
raise VolumeError('Could not determine the device name for bootstrap volume')

udev_name = (start_dev_names ^ end_dev_names).pop()
udev_path = log_check_call(['udevadm', 'info',
'--root', '--query=name', '--name',
udev_name])
if len(udev_path) != 1 or not os.path.exists(udev_path[0]):
raise VolumeError('Could not find device path for bootstrap volume')

self.device_path = udev_path[0]

def _before_detach(self, e):
self.conn.detach_volume(VolumeId=self.vol_id,
InstanceId=self.instance_id,
Expand Down