ansible-collections-openstack/plugins/modules/volume.py

264 lines
7.8 KiB
Python

#!/usr/bin/python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: volume
short_description: Create/Delete Cinder Volumes
author: OpenStack Ansible SIG
description:
- Create or Remove cinder block storage volumes
options:
size:
description:
- Size of volume in GB. This parameter is required when the
I(state) parameter is 'present'.
type: int
display_name:
description:
- Name of volume
required: true
type: str
aliases: [name]
display_description:
description:
- String describing the volume
type: str
aliases: [description]
volume_type:
description:
- Volume type for volume
type: str
image:
description:
- Image name or id for boot from volume
type: str
snapshot_id:
description:
- Volume snapshot id to create from
type: str
volume:
description:
- Volume name or id to create from
type: str
bootable:
description:
- Bootable flag for volume.
type: bool
default: False
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
scheduler_hints:
description:
- Scheduler hints passed to volume API in form of dict
type: dict
metadata:
description:
- Metadata for the volume
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Creates a new volume
- name: create a volume
hosts: localhost
tasks:
- name: create 40g test volume
openstack.cloud.volume:
state: present
cloud: mordred
availability_zone: az2
size: 40
display_name: test_volume
scheduler_hints:
same_host: 243e8d3c-8f47-4a61-93d6-7215c344b0c0
'''
RETURNS = '''
id:
description: Cinder's unique ID for this volume
returned: always
type: str
sample: fcc4ac1c-e249-4fe7-b458-2138bfb44c06
volume:
description: Cinder's representation of the volume object
returned: always
type: dict
sample: {'...'}
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class VolumeModule(OpenStackModule):
argument_spec = dict(
size=dict(type='int'),
volume_type=dict(type='str'),
display_name=dict(required=True, aliases=['name'], type='str'),
display_description=dict(aliases=['description'], type='str'),
image=dict(type='str'),
snapshot_id=dict(type='str'),
volume=dict(type='str'),
state=dict(default='present', choices=['absent', 'present'], type='str'),
scheduler_hints=dict(type='dict'),
metadata=dict(type='dict'),
bootable=dict(type='bool', default=False)
)
module_kwargs = dict(
mutually_exclusive=[
['image', 'snapshot_id', 'volume'],
],
required_if=[
['state', 'present', ['size']],
],
)
def _needs_update(self, volume):
'''
check for differences in updatable values, at the moment
openstacksdk only supports extending the volume size, this
may change in the future.
:returns: bool
'''
compare_simple = ['size']
for k in compare_simple:
if self.params[k] is not None and self.params[k] != volume.get(k):
return True
return False
def _modify_volume(self, volume):
'''
modify volume, the only modification to an existing volume
available at the moment is extending the size, this is
limited by the openstacksdk and may change whenever the
functionality is extended.
'''
volume = self.conn.get_volume(self.params['display_name'])
diff = {'before': volume, 'after': ''}
size = self.params['size']
if size < volume.get('size'):
self.fail_json(
msg='Cannot shrink volumes, size: {0} < {1}'.format(size, volume.get('size'))
)
if not self._needs_update(volume):
diff['after'] = volume
self.exit_json(changed=False, id=volume['id'], volume=volume, diff=diff)
if self.ansible.check_mode:
diff['after'] = volume
self.exit_json(changed=True, id=volume['id'], volume=volume, diff=diff)
self.conn.volume.extend_volume(
volume.id,
size
)
diff['after'] = self.conn.get_volume(self.params['display_name'])
self.exit_json(changed=True, id=volume['id'], volume=volume, diff=diff)
def _present_volume(self):
diff = {'before': '', 'after': ''}
volume_args = dict(
size=self.params['size'],
volume_type=self.params['volume_type'],
display_name=self.params['display_name'],
display_description=self.params['display_description'],
snapshot_id=self.params['snapshot_id'],
bootable=self.params['bootable'],
availability_zone=self.params['availability_zone'],
)
if self.params['image']:
image_id = self.conn.get_image_id(self.params['image'])
if not image_id:
self.fail_json(msg="Failed to find image '%s'" % self.params['image'])
volume_args['imageRef'] = image_id
if self.params['volume']:
volume_id = self.conn.get_volume_id(self.params['volume'])
if not volume_id:
self.fail_json(msg="Failed to find volume '%s'" % self.params['volume'])
volume_args['source_volid'] = volume_id
if self.params['scheduler_hints']:
volume_args['scheduler_hints'] = self.params['scheduler_hints']
if self.params['metadata']:
volume_args['metadata'] = self.params['metadata']
if self.ansible.check_mode:
diff['after'] = volume_args
self.exit_json(changed=True, id=None, volume=volume_args, diff=diff)
volume = self.conn.create_volume(
wait=self.params['wait'], timeout=self.params['timeout'],
**volume_args)
diff['after'] = volume
self.exit_json(changed=True, id=volume['id'], volume=volume, diff=diff)
def _absent_volume(self, volume):
changed = False
diff = {'before': '', 'after': ''}
if self.conn.volume_exists(self.params['display_name']):
volume = self.conn.get_volume(self.params['display_name'])
diff['before'] = volume
if self.ansible.check_mode:
self.exit_json(changed=True, diff=diff)
try:
changed = self.conn.delete_volume(name_or_id=self.params['display_name'],
wait=self.params['wait'],
timeout=self.params['timeout'])
except self.sdk.exceptions.ResourceTimeout:
diff['after'] = volume
self.exit_json(changed=changed, diff=diff)
self.exit_json(changed=changed, diff=diff)
def run(self):
state = self.params['state']
if self.conn.volume_exists(self.params['display_name']):
volume = self.conn.get_volume(self.params['display_name'])
else:
volume = None
if state == 'present':
if not volume:
self._present_volume()
elif self._needs_update(volume):
self._modify_volume(volume)
else:
self.exit_json(changed=False, id=volume['id'], volume=volume)
if state == 'absent':
self._absent_volume(volume)
def main():
module = VolumeModule()
module()
if __name__ == '__main__':
main()