diff options
Diffstat (limited to 'ansible_collections/openstack/cloud/plugins/modules/volume.py')
-rw-r--r-- | ansible_collections/openstack/cloud/plugins/modules/volume.py | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/ansible_collections/openstack/cloud/plugins/modules/volume.py b/ansible_collections/openstack/cloud/plugins/modules/volume.py new file mode 100644 index 00000000..3a50c05a --- /dev/null +++ b/ansible_collections/openstack/cloud/plugins/modules/volume.py @@ -0,0 +1,263 @@ +#!/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() |