Skip to content

Commit

Permalink
ec2_vol support MultiAttach disk (#365)
Browse files Browse the repository at this point in the history
ec2_vol support MultiAttach disk

SUMMARY

new parameter multi_attach to support MultiAttach on disk creation/update

#225
ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ec2_vol
ADDITIONAL INFORMATION

Reviewed-by: Markus Bergholz <None>
Reviewed-by: None <None>
Reviewed-by: Mark Chappell <None>
  • Loading branch information
abikouo authored Jun 24, 2021
1 parent dbec0b0 commit 1879a62
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
minor_changes:
- ec2_vol - add parameter ``multi_attach`` to support Multi-Attach on volume creation/update (https://github.com/ansible-collections/amazon.aws/pull/362).
breaking_changes:
- ec2_vol_info - return ``attachment_set`` is now a list of attachments with Multi-Attach support on disk. (https://github.com/ansible-collections/amazon.aws/pull/362).
91 changes: 62 additions & 29 deletions plugins/modules/ec2_vol.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@
- Requires at least botocore version 1.19.27.
type: int
version_added: 1.4.0
multi_attach:
description:
- If set to C(yes), Multi-Attach will be enabled when creating the volume.
- When you create a new volume, Multi-Attach is disabled by default.
- This parameter is supported with io1 and io2 volumes only.
type: bool
version_added: 2.0.0
author: "Lester Wade (@lwade)"
extends_documentation_fragment:
- amazon.aws.aws
Expand Down Expand Up @@ -189,6 +196,14 @@
volume_type: gp2
device_name: /dev/xvdf
# Create new volume with multi-attach enabled
- amazon.aws.ec2_vol:
zone: XXXXXX
multi_attach: true
volume_size: 4
volume_type: io1
iops: 102
# Attach an existing volume to instance. The volume will be deleted upon instance termination.
- amazon.aws.ec2_vol:
instance: XXXXXX
Expand Down Expand Up @@ -218,13 +233,13 @@
returned: when success
type: str
sample: {
"attachment_set": {
"attachment_set": [{
"attach_time": "2015-10-23T00:22:29.000Z",
"deleteOnTermination": "false",
"device": "/dev/sdf",
"instance_id": "i-8356263c",
"status": "attached"
},
}],
"create_time": "2015-10-21T14:36:08.870Z",
"encrypted": false,
"id": "vol-35b333d9",
Expand Down Expand Up @@ -408,14 +423,23 @@ def update_volume(module, ec2_conn, volume):
throughput_changed = True
req_obj['Throughput'] = target_throughput

changed = iops_changed or size_changed or type_changed or throughput_changed
target_multi_attach = module.params.get('multi_attach')
multi_attach_changed = False
if target_multi_attach is not None:
original_multi_attach = volume['multi_attach_enabled']
if target_multi_attach != original_multi_attach:
multi_attach_changed = True
req_obj['MultiAttachEnabled'] = target_multi_attach

changed = iops_changed or size_changed or type_changed or throughput_changed or multi_attach_changed

if changed:
response = ec2_conn.modify_volume(**req_obj)

volume['size'] = response.get('VolumeModification').get('TargetSize')
volume['volume_type'] = response.get('VolumeModification').get('TargetVolumeType')
volume['iops'] = response.get('VolumeModification').get('TargetIops')
volume['multi_attach_enabled'] = response.get('VolumeModification').get('TargetMultiAttachEnabled')
if module.botocore_at_least("1.19.27"):
volume['throughput'] = response.get('VolumeModification').get('TargetThroughput')

Expand All @@ -431,6 +455,7 @@ def create_volume(module, ec2_conn, zone):
volume_type = module.params.get('volume_type')
snapshot = module.params.get('snapshot')
throughput = module.params.get('throughput')
multi_attach = module.params.get('multi_attach')

volume = get_volume(module, ec2_conn)

Expand Down Expand Up @@ -458,6 +483,8 @@ def create_volume(module, ec2_conn, zone):

if throughput:
additional_params['Throughput'] = int(throughput)
if multi_attach:
additional_params['MultiAttachEnabled'] = True

create_vol_response = ec2_conn.create_volume(
aws_retry=True,
Expand Down Expand Up @@ -489,11 +516,13 @@ def attach_volume(module, ec2_conn, volume_dict, instance_dict, device_name):

attachment_data = get_attachment_data(volume_dict, wanted_state='attached')
if attachment_data:
if attachment_data.get('instance_id', None) != instance_dict['instance_id']:
module.fail_json(msg="Volume {0} is already attached to another instance: {1}".format(volume_dict['volume_id'],
attachment_data.get('instance_id', None)))
else:
return volume_dict, changed
if not volume_dict['multi_attach_enabled']:
# volumes without MultiAttach Enabled can be attached to 1 instance only
if attachment_data[0].get('instance_id', None) != instance_dict['instance_id']:
module.fail_json(msg="Volume {0} is already attached to another instance: {1}".format(volume_dict['volume_id'],
attachment_data[0].get('instance_id', None)))
else:
return volume_dict, changed

try:
attach_response = ec2_conn.attach_volume(aws_retry=True, Device=device_name,
Expand Down Expand Up @@ -557,17 +586,22 @@ def modify_dot_attribute(module, ec2_conn, instance_dict, device_name):
def get_attachment_data(volume_dict, wanted_state=None):
changed = False

attachment_data = {}
attachment_data = []
if not volume_dict:
return attachment_data
for data in volume_dict.get('attachments', []):
if wanted_state and wanted_state == data['state']:
attachment_data = data
break
else:
# No filter, return first
attachment_data = data
break
resource = volume_dict.get('attachments', [])
if wanted_state:
# filter 'state', return attachment matching wanted state
resource = [data for data in resource if data['state'] == wanted_state]

for data in resource:
attachment_data.append({
'attach_time': data.get('attach_time', None),
'device': data.get('device', None),
'instance_id': data.get('instance_id', None),
'status': data.get('state', None),
'delete_on_termination': data.get('delete_on_termination', None)
})

return attachment_data

Expand All @@ -576,8 +610,9 @@ def detach_volume(module, ec2_conn, volume_dict):
changed = False

attachment_data = get_attachment_data(volume_dict, wanted_state='attached')
if attachment_data:
ec2_conn.detach_volume(aws_retry=True, VolumeId=volume_dict['volume_id'])
# The ID of the instance must be specified if you are detaching a Multi-Attach enabled volume.
for attachment in attachment_data:
ec2_conn.detach_volume(aws_retry=True, InstanceId=attachment['instance_id'], VolumeId=volume_dict['volume_id'])
waiter = ec2_conn.get_waiter('volume_available')
waiter.wait(
VolumeIds=[volume_dict['volume_id']],
Expand All @@ -602,13 +637,8 @@ def get_volume_info(module, volume, tags=None):
'status': volume.get('state'),
'type': volume.get('volume_type'),
'zone': volume.get('availability_zone'),
'attachment_set': {
'attach_time': attachment_data.get('attach_time', None),
'device': attachment_data.get('device', None),
'instance_id': attachment_data.get('instance_id', None),
'status': attachment_data.get('state', None),
'deleteOnTermination': attachment_data.get('delete_on_termination', None)
},
'attachment_set': attachment_data,
'multi_attach_enabled': volume.get('multi_attach_enabled'),
'tags': tags
}

Expand Down Expand Up @@ -659,6 +689,7 @@ def main():
modify_volume=dict(default=False, type='bool'),
throughput=dict(type='int'),
purge_tags=dict(type='bool', default=False),
multi_attach=dict(type='bool'),
)

module = AnsibleAWSModule(
Expand All @@ -681,6 +712,7 @@ def main():
iops = module.params.get('iops')
volume_type = module.params.get('volume_type')
throughput = module.params.get('throughput')
multi_attach = module.params.get('multi_attach')

if state == 'list':
module.deprecate(
Expand Down Expand Up @@ -717,6 +749,9 @@ def main():
if throughput < 125 or throughput > 1000:
module.fail_json(msg='Throughput values must be between 125 and 1000.')

if multi_attach is True and volume_type not in ('io1', 'io2'):
module.fail_json(msg='multi_attach is only supported for io1 and io2 volumes.')

# Set changed flag
changed = False

Expand Down Expand Up @@ -777,8 +812,6 @@ def main():
changed=False
)

attach_state_changed = False

if volume:
volume, changed = update_volume(module, ec2_conn, volume)
else:
Expand All @@ -799,7 +832,7 @@ def main():
if tags_changed:
changed = True

module.exit_json(changed=changed, volume=volume_info, device=volume_info['attachment_set']['device'],
module.exit_json(changed=changed, volume=volume_info, device=device_name,
volume_id=volume_info['id'], volume_type=volume_info['type'])
elif state == 'absent':
if not name and not param_id:
Expand Down
25 changes: 18 additions & 7 deletions plugins/modules/ec2_vol_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@
description: The Availability Zone of the volume.
type: str
sample: "us-east-1b"
throughput:
description: The throughput that the volume supports, in MiB/s.
type: int
sample: 131
'''

try:
Expand All @@ -128,6 +132,16 @@ def get_volume_info(volume, region):

attachment = volume["attachments"]

attachment_data = []
for data in volume["attachments"]:
attachment_data.append({
'attach_time': data.get('attach_time', None),
'device': data.get('device', None),
'instance_id': data.get('instance_id', None),
'status': data.get('state', None),
'delete_on_termination': data.get('delete_on_termination', None)
})

volume_info = {
'create_time': volume["create_time"],
'id': volume["volume_id"],
Expand All @@ -139,16 +153,13 @@ def get_volume_info(volume, region):
'type': volume["volume_type"],
'zone': volume["availability_zone"],
'region': region,
'attachment_set': {
'attach_time': attachment[0]["attach_time"] if len(attachment) > 0 else None,
'device': attachment[0]["device"] if len(attachment) > 0 else None,
'instance_id': attachment[0]["instance_id"] if len(attachment) > 0 else None,
'status': attachment[0]["state"] if len(attachment) > 0 else None,
'delete_on_termination': attachment[0]["delete_on_termination"] if len(attachment) > 0 else None
},
'attachment_set': attachment_data,
'tags': boto3_tag_list_to_ansible_dict(volume['tags']) if "tags" in volume else None
}

if 'throughput' in volume:
volume_info['throughput'] = volume["throughput"]

return volume_info


Expand Down
Loading

0 comments on commit 1879a62

Please sign in to comment.