Updates volume for 2.0.0
Changes sdk calls to use the proxy layer where convenient. Ensures resources are converted to dict before returns. Changes the name of various parameters to match sdk attributes. Adds the old attribute names as aliases to maintain backward compatibility. Adds detailed return documentation. Removed the ability to create volumes in specific projects. Users looking to do that can use the auth parameter to specify what project is relevant Removes conditionals in tests that maintained support for ancient openstack and sdk releases. Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/854293 Change-Id: I3c4f4209f2ca6348370a45473bdb0d111b2439b6
This commit is contained in:
parent
99074ccd12
commit
c13fac3796
31
ci/roles/volume/defaults/main.yml
Normal file
31
ci/roles/volume/defaults/main.yml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
test_volume_image: ansible_test_volume_image
|
||||||
|
test_volume_shared_image: ansible_test_volume_shared_image
|
||||||
|
expected_fields:
|
||||||
|
- attachments
|
||||||
|
- availability_zone
|
||||||
|
- consistency_group_id
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- description
|
||||||
|
- extended_replication_status
|
||||||
|
- group_id
|
||||||
|
- host
|
||||||
|
- image_id
|
||||||
|
- is_bootable
|
||||||
|
- is_encrypted
|
||||||
|
- migration_id
|
||||||
|
- migration_status
|
||||||
|
- project_id
|
||||||
|
- replication_driver_data
|
||||||
|
- replication_status
|
||||||
|
- scheduler_hints
|
||||||
|
- size
|
||||||
|
- snapshot_id
|
||||||
|
- source_volume_id
|
||||||
|
- status
|
||||||
|
- user_id
|
||||||
|
- volume_image_metadata
|
||||||
|
- volume_type
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- metadata
|
@ -4,15 +4,29 @@
|
|||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: present
|
state: present
|
||||||
size: 1
|
size: 1
|
||||||
display_name: ansible_volume
|
name: ansible_volume
|
||||||
display_description: Test volume
|
description: Test volume
|
||||||
|
register: vol
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that: item in vol.volume
|
||||||
|
loop: "{{ expected_fields }}"
|
||||||
|
|
||||||
|
- name: Create volume from existing volume
|
||||||
|
openstack.cloud.volume:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
size: 1
|
||||||
|
volume: "{{ vol.volume.id }}"
|
||||||
|
name: ansible_volume1
|
||||||
|
description: Test volume
|
||||||
register: vol
|
register: vol
|
||||||
|
|
||||||
- name: Create volume snapshot
|
- name: Create volume snapshot
|
||||||
openstack.cloud.volume_snapshot:
|
openstack.cloud.volume_snapshot:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: present
|
state: present
|
||||||
display_name: ansible_volume_snapshot
|
name: ansible_volume_snapshot
|
||||||
volume: ansible_volume
|
volume: ansible_volume
|
||||||
register: vol_snap
|
register: vol_snap
|
||||||
|
|
||||||
@ -21,23 +35,30 @@
|
|||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
name: ansible_volume_snapshot
|
name: ansible_volume_snapshot
|
||||||
register: snap_info
|
register: snap_info
|
||||||
ignore_errors: sdk_version is version('0.49', '<')
|
|
||||||
|
- name: Create volume from snapshot
|
||||||
|
openstack.cloud.volume:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
size: 1
|
||||||
|
snapshot: ansible_volume_snapshot
|
||||||
|
name: ansible_volume2
|
||||||
|
description: Test volume
|
||||||
|
register: vol
|
||||||
|
|
||||||
- name: Create volume backup
|
- name: Create volume backup
|
||||||
openstack.cloud.volume_backup:
|
openstack.cloud.volume_backup:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: present
|
state: present
|
||||||
display_name: ansible_volume_backup
|
name: ansible_volume_backup
|
||||||
volume: ansible_volume
|
volume: ansible_volume
|
||||||
register: vol_backup
|
register: vol_backup
|
||||||
ignore_errors: sdk_version is version('0.49', '<')
|
|
||||||
|
|
||||||
- name: Get backup info
|
- name: Get backup info
|
||||||
openstack.cloud.volume_backup_info:
|
openstack.cloud.volume_backup_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
name: ansible_volume_backup
|
name: ansible_volume_backup
|
||||||
register: backup_info
|
register: backup_info
|
||||||
ignore_errors: sdk_version is version('0.49', '<')
|
|
||||||
|
|
||||||
- debug: var=vol
|
- debug: var=vol
|
||||||
|
|
||||||
@ -50,14 +71,14 @@
|
|||||||
- name: Delete volume backup
|
- name: Delete volume backup
|
||||||
openstack.cloud.volume_backup:
|
openstack.cloud.volume_backup:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
display_name: ansible_volume_backup
|
name: ansible_volume_backup
|
||||||
|
wait: false
|
||||||
state: absent
|
state: absent
|
||||||
ignore_errors: sdk_version is version(0.49, '<')
|
|
||||||
|
|
||||||
- name: Delete volume snapshot
|
- name: Delete volume snapshot
|
||||||
openstack.cloud.volume_snapshot:
|
openstack.cloud.volume_snapshot:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
display_name: ansible_volume_snapshot
|
name: ansible_volume_snapshot
|
||||||
volume: ansible_volume
|
volume: ansible_volume
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
@ -65,6 +86,71 @@
|
|||||||
openstack.cloud.volume:
|
openstack.cloud.volume:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: absent
|
state: absent
|
||||||
display_name: ansible_volume
|
name: ansible_volume
|
||||||
|
|
||||||
|
- name: Clean up
|
||||||
|
openstack.cloud.volume:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- ansible_volume1
|
||||||
|
- ansible_volume2
|
||||||
|
|
||||||
|
- name: Create a test image file
|
||||||
|
shell: mktemp
|
||||||
|
register: tmp_file
|
||||||
|
|
||||||
|
- name: Fill test image file to 1MB
|
||||||
|
shell: truncate -s 1048576 {{ tmp_file.stdout }}
|
||||||
|
|
||||||
|
- name: Create test image
|
||||||
|
openstack.cloud.image:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ test_volume_image }}"
|
||||||
|
filename: "{{ tmp_file.stdout }}"
|
||||||
|
disk_format: raw
|
||||||
|
tags: "{{ image_tags }}"
|
||||||
|
register: returned_image
|
||||||
|
|
||||||
|
- name: Create volume from image
|
||||||
|
openstack.cloud.volume:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
size: 1
|
||||||
|
image: "{{ test_volume_image }}"
|
||||||
|
name: ansible_volume2
|
||||||
|
description: Test volume
|
||||||
|
register: vol
|
||||||
|
|
||||||
|
- name: Delete volume from image
|
||||||
|
openstack.cloud.volume:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: ansible_volume2
|
||||||
|
state: absent
|
||||||
|
register: vol
|
||||||
|
|
||||||
|
- name: Create test shared image
|
||||||
|
openstack.cloud.image:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ image_name }}"
|
||||||
|
filename: "{{ tmp_file.stdout }}"
|
||||||
|
is_public: true
|
||||||
|
disk_format: raw
|
||||||
|
tags: "{{ image_tags }}"
|
||||||
|
register: returned_image
|
||||||
|
|
||||||
|
- name: Delete test shared image
|
||||||
|
openstack.cloud.image:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: "{{ image_name }}"
|
||||||
|
filename: "{{ tmp_file.stdout }}"
|
||||||
|
is_public: true
|
||||||
|
disk_format: raw
|
||||||
|
tags: "{{ image_tags }}"
|
||||||
|
register: returned_image
|
||||||
|
|
||||||
- include_tasks: volume_info.yml
|
- include_tasks: volume_info.yml
|
||||||
|
@ -3,87 +3,60 @@
|
|||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: true
|
details: true
|
||||||
all_projects: true
|
all_projects: true
|
||||||
register: all_sdk
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- name: Check info for all projects
|
- name: Get info about volumes
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
# Rocky SDK doesn't have all_projects attribute
|
|
||||||
- >-
|
|
||||||
(all_sdk is failed and sdk_version is version(0.19, '<')) or
|
|
||||||
all_sdk is success
|
|
||||||
|
|
||||||
- name: Get info about volumes for all SDK
|
|
||||||
openstack.cloud.volume_info:
|
|
||||||
cloud: "{{ cloud }}"
|
|
||||||
details: true
|
|
||||||
register: all_sdk1
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- name: Check info for all SDK
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- all_sdk1 is success
|
|
||||||
- all_sdk1.volumes is defined
|
|
||||||
|
|
||||||
- name: Run tests for SDK > 0.28 (from train)
|
|
||||||
when: sdk_version is version(0.28, '>')
|
|
||||||
block:
|
|
||||||
|
|
||||||
- name: Get info about volumes
|
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
all_projects: true
|
all_projects: true
|
||||||
register: delete
|
register: delete
|
||||||
|
|
||||||
- name: Clean up volumes before the test
|
- name: Clean up volumes before the test
|
||||||
openstack.cloud.volume:
|
openstack.cloud.volume:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: absent
|
state: absent
|
||||||
display_name: "{{ vol.name }}"
|
name: "{{ vol.name }}"
|
||||||
loop: "{{ delete.volumes }}"
|
loop: "{{ delete.volumes }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: vol
|
loop_var: vol
|
||||||
|
|
||||||
- name: Create volume
|
- name: Create volume
|
||||||
openstack.cloud.volume:
|
openstack.cloud.volume:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: present
|
state: present
|
||||||
size: 1
|
size: 1
|
||||||
display_name: ansible_test
|
name: ansible_test
|
||||||
display_description: testci
|
description: testci
|
||||||
register: vol
|
register: vol
|
||||||
|
|
||||||
- name: Get info about volumes
|
- name: Get info about volumes
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: true
|
details: true
|
||||||
all_projects: true
|
all_projects: true
|
||||||
register: info
|
register: info
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info.volumes | selectattr("description", "equalto", "testci") | list | length == 1
|
- info.volumes | selectattr("description", "equalto", "testci") | list | length == 1
|
||||||
- info.volumes.0.name == 'ansible_test'
|
- info.volumes.0.name == 'ansible_test'
|
||||||
- info.volumes.0.status == 'available'
|
- info.volumes.0.status == 'available'
|
||||||
|
|
||||||
- name: Get not detailed info about volumes
|
- name: Get not detailed info about volumes
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: false
|
details: false
|
||||||
all_projects: true
|
all_projects: true
|
||||||
register: info1
|
register: info1
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info1.volumes | selectattr("id", "equalto", "{{ info.volumes.0.id }}") | list | length == 1
|
- info1.volumes | selectattr("id", "equalto", "{{ info.volumes.0.id }}") | list | length == 1
|
||||||
- info1.volumes.0.name == 'ansible_test'
|
- info1.volumes.0.name == 'ansible_test'
|
||||||
- info1.volumes.0.status == None
|
- info1.volumes.0.status == None
|
||||||
|
|
||||||
- name: Get info about volumes with name
|
- name: Get info about volumes with name
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: false
|
details: false
|
||||||
@ -91,13 +64,13 @@
|
|||||||
all_projects: true
|
all_projects: true
|
||||||
register: info2
|
register: info2
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info2.volumes | length == 1
|
- info2.volumes | length == 1
|
||||||
- info2.volumes.0.name == 'ansible_test'
|
- info2.volumes.0.name == 'ansible_test'
|
||||||
|
|
||||||
- name: Get info about volumes with non-existent name
|
- name: Get info about volumes with non-existent name
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: false
|
details: false
|
||||||
@ -105,12 +78,12 @@
|
|||||||
all_projects: true
|
all_projects: true
|
||||||
register: info3
|
register: info3
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info3.volumes | length == 0
|
- info3.volumes | length == 0
|
||||||
|
|
||||||
- name: Get info about volumes
|
- name: Get info about volumes
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: false
|
details: false
|
||||||
@ -118,38 +91,38 @@
|
|||||||
all_projects: true
|
all_projects: true
|
||||||
register: info4
|
register: info4
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info4.volumes | length == 1
|
- info4.volumes | length == 1
|
||||||
- info4.volumes.0.name == 'ansible_test'
|
- info4.volumes.0.name == 'ansible_test'
|
||||||
|
|
||||||
- name: Get info about volumes not from all projects
|
- name: Get info about volumes not from all projects
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
details: false
|
details: false
|
||||||
name: ansible_test
|
name: ansible_test
|
||||||
register: info4a
|
register: info4a
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info4a.volumes | length == 1
|
- info4a.volumes | length == 1
|
||||||
- info4a.volumes.0.name == 'ansible_test'
|
- info4a.volumes.0.name == 'ansible_test'
|
||||||
|
|
||||||
- name: Delete volume
|
- name: Delete volume
|
||||||
openstack.cloud.volume:
|
openstack.cloud.volume:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: absent
|
state: absent
|
||||||
display_name: ansible_test
|
name: ansible_test
|
||||||
|
|
||||||
- name: Get info when no volumes
|
- name: Get info when no volumes
|
||||||
openstack.cloud.volume_info:
|
openstack.cloud.volume_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
all_projects: true
|
all_projects: true
|
||||||
register: info5
|
register: info5
|
||||||
|
|
||||||
- name: Check info
|
- name: Check info
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- info5.volumes | selectattr("name", "equalto", "ansible_test") | list | length == 0
|
- info5.volumes | selectattr("name", "equalto", "ansible_test") | list | length == 0
|
||||||
|
@ -16,57 +16,62 @@ options:
|
|||||||
description:
|
description:
|
||||||
- The availability zone.
|
- The availability zone.
|
||||||
type: str
|
type: str
|
||||||
|
description:
|
||||||
|
description:
|
||||||
|
- String describing the volume
|
||||||
|
type: str
|
||||||
|
aliases: [display_description]
|
||||||
|
image:
|
||||||
|
description:
|
||||||
|
- Image name or id for boot from volume
|
||||||
|
- Mutually exclusive with I(snapshot) and I(volume)
|
||||||
|
type: str
|
||||||
|
is_bootable:
|
||||||
|
description:
|
||||||
|
- Bootable flag for volume.
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
aliases: [bootable]
|
||||||
|
metadata:
|
||||||
|
description:
|
||||||
|
- Metadata for the volume
|
||||||
|
type: dict
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- Name of volume
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
aliases: [display_name]
|
||||||
|
scheduler_hints:
|
||||||
|
description:
|
||||||
|
- Scheduler hints passed to volume API in form of dict
|
||||||
|
type: dict
|
||||||
size:
|
size:
|
||||||
description:
|
description:
|
||||||
- Size of volume in GB. This parameter is required when the
|
- Size of volume in GB. This parameter is required when the
|
||||||
I(state) parameter is 'present'.
|
I(state) parameter is 'present'.
|
||||||
type: int
|
type: int
|
||||||
display_name:
|
snapshot:
|
||||||
description:
|
description:
|
||||||
- Name of volume
|
- Volume snapshot name or id to create from
|
||||||
required: true
|
- Mutually exclusive with I(image) and I(volume)
|
||||||
type: str
|
type: str
|
||||||
aliases: [name]
|
aliases: [snapshot_id]
|
||||||
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:
|
state:
|
||||||
description:
|
description:
|
||||||
- Should the resource be present or absent.
|
- Should the resource be present or absent.
|
||||||
choices: [present, absent]
|
choices: [present, absent]
|
||||||
default: present
|
default: present
|
||||||
type: str
|
type: str
|
||||||
scheduler_hints:
|
volume:
|
||||||
description:
|
description:
|
||||||
- Scheduler hints passed to volume API in form of dict
|
- Volume name or id to create from
|
||||||
type: dict
|
- Mutually exclusive with I(image) and I(snapshot)
|
||||||
metadata:
|
type: str
|
||||||
|
volume_type:
|
||||||
description:
|
description:
|
||||||
- Metadata for the volume
|
- Volume type for volume
|
||||||
type: dict
|
type: str
|
||||||
requirements:
|
requirements:
|
||||||
- "python >= 3.6"
|
- "python >= 3.6"
|
||||||
- "openstacksdk"
|
- "openstacksdk"
|
||||||
@ -77,187 +82,255 @@ extends_documentation_fragment:
|
|||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
# Creates a new volume
|
# Creates a new volume
|
||||||
- name: create a volume
|
- name: create 40g test volume
|
||||||
hosts: localhost
|
|
||||||
tasks:
|
|
||||||
- name: create 40g test volume
|
|
||||||
openstack.cloud.volume:
|
openstack.cloud.volume:
|
||||||
state: present
|
state: present
|
||||||
cloud: mordred
|
cloud: mordred
|
||||||
availability_zone: az2
|
availability_zone: az2
|
||||||
size: 40
|
size: 40
|
||||||
display_name: test_volume
|
name: test_volume
|
||||||
scheduler_hints:
|
scheduler_hints:
|
||||||
same_host: 243e8d3c-8f47-4a61-93d6-7215c344b0c0
|
same_host: 243e8d3c-8f47-4a61-93d6-7215c344b0c0
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURNS = '''
|
RETURN = '''
|
||||||
id:
|
|
||||||
description: Cinder's unique ID for this volume
|
|
||||||
returned: always
|
|
||||||
type: str
|
|
||||||
sample: fcc4ac1c-e249-4fe7-b458-2138bfb44c06
|
|
||||||
|
|
||||||
volume:
|
volume:
|
||||||
description: Cinder's representation of the volume object
|
description: Cinder's representation of the volume object
|
||||||
returned: always
|
returned: always
|
||||||
type: dict
|
type: dict
|
||||||
sample: {'...'}
|
contains:
|
||||||
|
attachments:
|
||||||
|
description: Instance attachment information. If this volume is attached
|
||||||
|
to a server instance, the attachments list includes the UUID
|
||||||
|
of the attached server, an attachment UUID, the name of the
|
||||||
|
attached host, if any, the volume UUID, the device, and the
|
||||||
|
device UUID. Otherwise, this list is empty.
|
||||||
|
type: list
|
||||||
|
availability_zone:
|
||||||
|
description: The name of the availability zone.
|
||||||
|
type: str
|
||||||
|
consistency_group_id:
|
||||||
|
description: The UUID of the consistency group.
|
||||||
|
type: str
|
||||||
|
created_at:
|
||||||
|
description: The date and time when the resource was created.
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
description: The volume description.
|
||||||
|
type: str
|
||||||
|
extended_replication_status:
|
||||||
|
description: Extended replication status on this volume.
|
||||||
|
type: str
|
||||||
|
group_id:
|
||||||
|
description: The ID of the group.
|
||||||
|
type: str
|
||||||
|
host:
|
||||||
|
description: The volume's current back-end.
|
||||||
|
type: str
|
||||||
|
id:
|
||||||
|
description: The UUID of the volume.
|
||||||
|
type: str
|
||||||
|
image_id:
|
||||||
|
description: Image on which the volume was based
|
||||||
|
type: str
|
||||||
|
is_bootable:
|
||||||
|
description: Enables or disables the bootable attribute. You can boot an
|
||||||
|
instance from a bootable volume.
|
||||||
|
type: str
|
||||||
|
is_encrypted:
|
||||||
|
description: If true, this volume is encrypted.
|
||||||
|
type: bool
|
||||||
|
metadata:
|
||||||
|
description: A metadata object. Contains one or more metadata key and
|
||||||
|
value pairs that are associated with the volume.
|
||||||
|
type: dict
|
||||||
|
migration_id:
|
||||||
|
description: The volume ID that this volume name on the backend is
|
||||||
|
based on.
|
||||||
|
type: str
|
||||||
|
migration_status:
|
||||||
|
description: The status of this volume migration (None means that a
|
||||||
|
migration is not currently in progress).
|
||||||
|
type: str
|
||||||
|
name:
|
||||||
|
description: The volume name.
|
||||||
|
type: str
|
||||||
|
project_id:
|
||||||
|
description: The project ID which the volume belongs to.
|
||||||
|
type: str
|
||||||
|
replication_driver_data:
|
||||||
|
description: Data set by the replication driver
|
||||||
|
type: str
|
||||||
|
replication_status:
|
||||||
|
description: The volume replication status.
|
||||||
|
type: str
|
||||||
|
scheduler_hints:
|
||||||
|
description: Scheduler hints for the volume
|
||||||
|
type: dict
|
||||||
|
size:
|
||||||
|
description: The size of the volume, in gibibytes (GiB).
|
||||||
|
type: int
|
||||||
|
snapshot_id:
|
||||||
|
description: To create a volume from an existing snapshot, specify the
|
||||||
|
UUID of the volume snapshot. The volume is created in same
|
||||||
|
availability zone and with same size as the snapshot.
|
||||||
|
type: str
|
||||||
|
source_volume_id:
|
||||||
|
description: The UUID of the source volume. The API creates a new volume
|
||||||
|
with the same size as the source volume unless a larger size
|
||||||
|
is requested.
|
||||||
|
type: str
|
||||||
|
status:
|
||||||
|
description: The volume status.
|
||||||
|
type: str
|
||||||
|
updated_at:
|
||||||
|
description: The date and time when the resource was updated.
|
||||||
|
type: str
|
||||||
|
user_id:
|
||||||
|
description: The UUID of the user.
|
||||||
|
type: str
|
||||||
|
volume_image_metadata:
|
||||||
|
description: List of image metadata entries. Only included for volumes
|
||||||
|
that were created from an image, or from a snapshot of a
|
||||||
|
volume originally created from an image.
|
||||||
|
type: dict
|
||||||
|
volume_type:
|
||||||
|
description: The associated volume type name for the volume.
|
||||||
|
type: str
|
||||||
'''
|
'''
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
class VolumeModule(OpenStackModule):
|
class VolumeModule(OpenStackModule):
|
||||||
|
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
availability_zone=dict(type='str'),
|
availability_zone=dict(),
|
||||||
size=dict(type='int'),
|
description=dict(aliases=['display_description']),
|
||||||
volume_type=dict(),
|
|
||||||
display_name=dict(required=True, aliases=['name']),
|
|
||||||
display_description=dict(aliases=['description']),
|
|
||||||
image=dict(),
|
image=dict(),
|
||||||
snapshot_id=dict(),
|
is_bootable=dict(type='bool', default=False, aliases=['bootable']),
|
||||||
volume=dict(),
|
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
|
||||||
scheduler_hints=dict(type='dict'),
|
|
||||||
metadata=dict(type='dict'),
|
metadata=dict(type='dict'),
|
||||||
bootable=dict(type='bool', default=False)
|
name=dict(required=True, aliases=['display_name']),
|
||||||
|
scheduler_hints=dict(type='dict'),
|
||||||
|
size=dict(type='int'),
|
||||||
|
snapshot=dict(aliases=['snapshot_id']),
|
||||||
|
state=dict(default='present', choices=['absent', 'present'], type='str'),
|
||||||
|
volume=dict(),
|
||||||
|
volume_type=dict(),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = dict(
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True,
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['image', 'snapshot_id', 'volume'],
|
['image', 'snapshot', 'volume'],
|
||||||
],
|
],
|
||||||
required_if=[
|
required_if=[
|
||||||
['state', 'present', ['size']],
|
['state', 'present', ['size']],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _needs_update(self, volume):
|
def _build_update(self, volume):
|
||||||
'''
|
keys = ('size',)
|
||||||
check for differences in updatable values, at the moment
|
return {k: self.params[k] for k in keys if self.params[k] is not None
|
||||||
openstacksdk only supports extending the volume size, this
|
and self.params[k] != volume[k]}
|
||||||
may change in the future.
|
|
||||||
:returns: bool
|
|
||||||
'''
|
|
||||||
compare_simple = ['size']
|
|
||||||
|
|
||||||
for k in compare_simple:
|
def _update(self, volume):
|
||||||
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
|
modify volume, the only modification to an existing volume
|
||||||
available at the moment is extending the size, this is
|
available at the moment is extending the size, this is
|
||||||
limited by the openstacksdk and may change whenever the
|
limited by the openstacksdk and may change whenever the
|
||||||
functionality is extended.
|
functionality is extended.
|
||||||
'''
|
'''
|
||||||
volume = self.conn.get_volume(self.params['display_name'])
|
diff = {'before': volume.to_dict(computed=False), 'after': ''}
|
||||||
diff = {'before': volume, 'after': ''}
|
diff['after'] = diff['before']
|
||||||
size = self.params['size']
|
|
||||||
|
|
||||||
if size < volume.get('size'):
|
update = self._build_update(volume)
|
||||||
self.fail_json(
|
|
||||||
msg='Cannot shrink volumes, size: {0} < {1}'.format(size, volume.get('size'))
|
|
||||||
)
|
|
||||||
|
|
||||||
if not self._needs_update(volume):
|
if not update:
|
||||||
diff['after'] = volume
|
self.exit_json(changed=False,
|
||||||
self.exit_json(changed=False, id=volume['id'], volume=volume, diff=diff)
|
volume=volume.to_dict(computed=False), diff=diff)
|
||||||
|
|
||||||
if self.ansible.check_mode:
|
if self.ansible.check_mode:
|
||||||
|
volume.size = update['size']
|
||||||
|
self.exit_json(changed=False,
|
||||||
|
volume=volume.to_dict(computed=False), diff=diff)
|
||||||
|
|
||||||
|
if 'size' in update and update['size'] != volume.size:
|
||||||
|
size = update['size']
|
||||||
|
self.conn.volume.extend_volume(volume.id, size)
|
||||||
|
volume = self.conn.block_storage.get_volume(volume)
|
||||||
|
|
||||||
|
volume = volume.to_dict(computed=False)
|
||||||
diff['after'] = volume
|
diff['after'] = volume
|
||||||
self.exit_json(changed=True, id=volume['id'], volume=volume, diff=diff)
|
self.exit_json(changed=True, volume=volume, diff=diff)
|
||||||
|
|
||||||
self.conn.volume.extend_volume(
|
def _build_create_kwargs(self):
|
||||||
volume.id,
|
keys = ('availability_zone', 'size', 'name', 'description',
|
||||||
size
|
'volume_type', 'scheduler_hints', 'metadata')
|
||||||
)
|
kwargs = {k: self.params[k] for k in keys
|
||||||
diff['after'] = self.conn.get_volume(self.params['display_name'])
|
if self.params[k] is not None}
|
||||||
self.exit_json(changed=True, id=volume['id'], volume=volume, diff=diff)
|
|
||||||
|
|
||||||
def _present_volume(self):
|
find_filters = {}
|
||||||
|
|
||||||
diff = {'before': '', 'after': ''}
|
if self.params['snapshot']:
|
||||||
|
snapshot = self.conn.block_storage.find_snapshot(
|
||||||
|
self.params['snapshot'], ignore_missing=False, **find_filters)
|
||||||
|
kwargs['snapshot_id'] = snapshot.id
|
||||||
|
|
||||||
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']:
|
if self.params['image']:
|
||||||
image_id = self.conn.get_image_id(self.params['image'])
|
image = self.conn.image.find_image(
|
||||||
if not image_id:
|
self.params['image'], ignore_missing=False)
|
||||||
self.fail_json(msg="Failed to find image '%s'" % self.params['image'])
|
kwargs['image_id'] = image.id
|
||||||
volume_args['imageRef'] = image_id
|
|
||||||
|
|
||||||
if self.params['volume']:
|
if self.params['volume']:
|
||||||
volume_id = self.conn.get_volume_id(self.params['volume'])
|
volume = self.conn.block_storage.find_volume(
|
||||||
if not volume_id:
|
self.params['volume'], ignore_missing=False, **find_filters)
|
||||||
self.fail_json(msg="Failed to find volume '%s'" % self.params['volume'])
|
kwargs['source_volume_id'] = volume.id
|
||||||
volume_args['source_volid'] = volume_id
|
|
||||||
|
|
||||||
if self.params['scheduler_hints']:
|
return kwargs
|
||||||
volume_args['scheduler_hints'] = self.params['scheduler_hints']
|
|
||||||
|
|
||||||
if self.params['metadata']:
|
def _create(self):
|
||||||
volume_args['metadata'] = self.params['metadata']
|
diff = {'before': '', 'after': ''}
|
||||||
|
volume_args = self._build_create_kwargs()
|
||||||
|
|
||||||
if self.ansible.check_mode:
|
if self.ansible.check_mode:
|
||||||
diff['after'] = volume_args
|
diff['after'] = volume_args
|
||||||
self.exit_json(changed=True, id=None, volume=volume_args, diff=diff)
|
self.exit_json(changed=True, volume=volume_args, diff=diff)
|
||||||
|
|
||||||
volume = self.conn.create_volume(
|
volume = self.conn.block_storage.create_volume(**volume_args)
|
||||||
wait=self.params['wait'], timeout=self.params['timeout'],
|
if self.params['wait']:
|
||||||
**volume_args)
|
self.conn.block_storage.wait_for_status(
|
||||||
|
volume, wait=self.params['timeout'])
|
||||||
|
|
||||||
|
volume = volume.to_dict(computed=False)
|
||||||
diff['after'] = volume
|
diff['after'] = volume
|
||||||
self.exit_json(changed=True, id=volume['id'], volume=volume, diff=diff)
|
self.exit_json(changed=True, volume=volume, diff=diff)
|
||||||
|
|
||||||
def _absent_volume(self, volume):
|
def _delete(self, volume):
|
||||||
changed = False
|
|
||||||
diff = {'before': '', 'after': ''}
|
diff = {'before': '', 'after': ''}
|
||||||
|
if volume is None:
|
||||||
|
self.exit_json(changed=False, diff=diff)
|
||||||
|
|
||||||
if self.conn.volume_exists(self.params['display_name']):
|
diff['before'] = volume.to_dict(computed=False)
|
||||||
volume = self.conn.get_volume(self.params['display_name'])
|
|
||||||
diff['before'] = volume
|
|
||||||
|
|
||||||
if self.ansible.check_mode:
|
if self.ansible.check_mode:
|
||||||
self.exit_json(changed=True, diff=diff)
|
self.exit_json(changed=True, diff=diff)
|
||||||
|
|
||||||
try:
|
self.conn.block_storage.delete_volume(volume)
|
||||||
changed = self.conn.delete_volume(name_or_id=self.params['display_name'],
|
if self.params['wait']:
|
||||||
wait=self.params['wait'],
|
self.conn.block_storage.wait_for_delete(
|
||||||
timeout=self.params['timeout'])
|
volume, wait=self.params['timeout'])
|
||||||
except self.sdk.exceptions.ResourceTimeout:
|
self.exit_json(changed=True, diff=diff)
|
||||||
diff['after'] = volume
|
|
||||||
self.exit_json(changed=changed, diff=diff)
|
|
||||||
|
|
||||||
self.exit_json(changed=changed, diff=diff)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
state = self.params['state']
|
state = self.params['state']
|
||||||
if self.conn.volume_exists(self.params['display_name']):
|
volume = self.conn.block_storage.find_volume(self.params['name'])
|
||||||
volume = self.conn.get_volume(self.params['display_name'])
|
|
||||||
else:
|
|
||||||
volume = None
|
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not volume:
|
if not volume:
|
||||||
self._present_volume()
|
self._create()
|
||||||
elif self._needs_update(volume):
|
|
||||||
self._modify_volume(volume)
|
|
||||||
else:
|
else:
|
||||||
self.exit_json(changed=False, id=volume['id'], volume=volume)
|
self._update(volume)
|
||||||
if state == 'absent':
|
if state == 'absent':
|
||||||
self._absent_volume(volume)
|
self._delete(volume)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user