Fix netapp_e_volume wait-for-initialization to complete issue. (#58304)

* Fix netapp_e_volume wait-for-initialization to complete issue.

* Add unit tests for wait_for operations in netapp_e_volume module.
pull/60217/head
Nathan Swartz 5 years ago committed by anshulbehl
parent d00aaf66d7
commit e80f8048ee

@ -130,13 +130,13 @@ options:
- This option has no effect on thinly provisioned volumes since the architecture for thin volumes cannot - This option has no effect on thinly provisioned volumes since the architecture for thin volumes cannot
benefit from read ahead caching. benefit from read ahead caching.
type: bool type: bool
default: false default: true
version_added: 2.8 version_added: 2.8
write_cache_enable: write_cache_enable:
description: description:
- Indicates whether write-back caching should be enabled for the volume. - Indicates whether write-back caching should be enabled for the volume.
type: bool type: bool
default: false default: true
version_added: 2.8 version_added: 2.8
workload_name: workload_name:
description: description:
@ -164,6 +164,13 @@ options:
type: bool type: bool
default: false default: false
version_added: 2.8 version_added: 2.8
initialization_timeout:
description:
- Duration in seconds before the wait_for_initialization operation will terminate.
- M(wait_for_initialization==True) to have any effect on module's operations.
type: int
required: false
version_added: 2.9
""" """
EXAMPLES = """ EXAMPLES = """
- name: Create simple volume with workload tags (volume meta data) - name: Create simple volume with workload tags (volume meta data)
@ -244,9 +251,7 @@ msg:
returned: always returned: always
sample: "Standard volume [workload_vol_1] has been created." sample: "Standard volume [workload_vol_1] has been created."
""" """
from time import sleep
import time
from ansible.module_utils.netapp import NetAppESeriesModule from ansible.module_utils.netapp import NetAppESeriesModule
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -271,11 +276,12 @@ class NetAppESeriesVolume(NetAppESeriesModule):
thin_volume_expansion_policy=dict(type="str", choices=["automatic", "manual"]), thin_volume_expansion_policy=dict(type="str", choices=["automatic", "manual"]),
thin_volume_growth_alert_threshold=dict(type="int", default=95), thin_volume_growth_alert_threshold=dict(type="int", default=95),
read_cache_enable=dict(type="bool", default=True), read_cache_enable=dict(type="bool", default=True),
read_ahead_enable=dict(type="bool", default=False), read_ahead_enable=dict(type="bool", default=True),
write_cache_enable=dict(type="bool", default=False), write_cache_enable=dict(type="bool", default=True),
workload_name=dict(type="str", required=False), workload_name=dict(type="str", required=False),
metadata=dict(type="dict", require=False), metadata=dict(type="dict", require=False),
wait_for_initialization=dict(type="bool", default=False)) wait_for_initialization=dict(type="bool", default=False),
initialization_timeout=dict(type="int", required=False))
required_if = [ required_if = [
["state", "present", ["storage_pool_name", "size"]], ["state", "present", ["storage_pool_name", "size"]],
@ -317,6 +323,7 @@ class NetAppESeriesVolume(NetAppESeriesModule):
self.workload_name = args["workload_name"] self.workload_name = args["workload_name"]
self.metadata = args["metadata"] self.metadata = args["metadata"]
self.wait_for_initialization = args["wait_for_initialization"] self.wait_for_initialization = args["wait_for_initialization"]
self.initialization_timeout = args["initialization_timeout"]
# convert metadata to a list of dictionaries containing the keys "key" and "value" corresponding to # convert metadata to a list of dictionaries containing the keys "key" and "value" corresponding to
# each of the workload attributes dictionary entries # each of the workload attributes dictionary entries
@ -381,37 +388,44 @@ class NetAppESeriesVolume(NetAppESeriesModule):
self.module.fail_json(msg="Timed out waiting for the volume %s to become available. Array [%s]." self.module.fail_json(msg="Timed out waiting for the volume %s to become available. Array [%s]."
% (self.name, self.ssid)) % (self.name, self.ssid))
if not self.get_volume(): if not self.get_volume():
time.sleep(5) sleep(5)
self.wait_for_volume_availability(retries=retries - 1) self.wait_for_volume_availability(retries=retries - 1)
def wait_for_volume_action(self, timeout=None): def wait_for_volume_action(self, timeout=None):
"""Waits until volume action is complete is complete. """Waits until volume action is complete is complete.
:param: int timeout: Wait duration measured in seconds. Waits indefinitely when None. :param: int timeout: Wait duration measured in seconds. Waits indefinitely when None.
""" """
action = None action = "unknown"
percent_complete = None percent_complete = None
while action != "complete":
while action != 'none': sleep(5)
time.sleep(5)
try: try:
rc, expansion = self.request("storage-systems/%s/volumes/%s/expand" rc, operations = self.request("storage-systems/%s/symbol/getLongLivedOpsProgress" % self.ssid)
% (self.ssid, self.volume_detail["id"]))
action = expansion["action"] # Search long lived operations for volume
percent_complete = expansion["percentComplete"] action = "complete"
for operation in operations["longLivedOpsProgress"]:
if operation["volAction"] is not None:
for key in operation.keys():
if (operation[key] is not None and "volumeRef" in operation[key] and
(operation[key]["volumeRef"] == self.volume_detail["id"] or
("storageVolumeRef" in self.volume_detail and operation[key]["volumeRef"] == self.volume_detail["storageVolumeRef"]))):
action = operation["volAction"]
percent_complete = operation["init"]["percentComplete"]
except Exception as err: except Exception as err:
self.module.fail_json(msg="Failed to get volume expansion progress. Volume [%s]. Array Id [%s]." self.module.fail_json(msg="Failed to get volume expansion progress. Volume [%s]. Array Id [%s]."
" Error[%s]." % (self.name, self.ssid, to_native(err))) " Error[%s]." % (self.name, self.ssid, to_native(err)))
if timeout <= 0: if timeout is not None:
self.module.warn("Expansion action, %s, failed to complete during the allotted time. Time remaining" if timeout <= 0:
" [%s]. Array Id [%s]." % (action, percent_complete, self.ssid)) self.module.warn("Expansion action, %s, failed to complete during the allotted time. Time remaining"
self.module.fail_json(msg="Expansion action failed to complete. Time remaining [%s]. Array Id [%s]." " [%s]. Array Id [%s]." % (action, percent_complete, self.ssid))
% (percent_complete, self.ssid)) self.module.fail_json(msg="Expansion action failed to complete. Time remaining [%s]. Array Id [%s]." % (percent_complete, self.ssid))
if timeout: if timeout:
timeout -= 5 timeout -= 5
self.module.log("Expansion action, %s, is %s complete." % (action, percent_complete))
self.module.log("Expansion action, %s, is %s complete." % (action, percent_complete))
self.module.log("Expansion action is complete.") self.module.log("Expansion action is complete.")
def get_storage_pool(self): def get_storage_pool(self):
@ -709,10 +723,6 @@ class NetAppESeriesVolume(NetAppESeriesModule):
self.module.fail_json(msg="Failed to expand volume. Volume [%s]. Array Id [%s]. Error[%s]." self.module.fail_json(msg="Failed to expand volume. Volume [%s]. Array Id [%s]. Error[%s]."
% (self.name, self.ssid, to_native(err))) % (self.name, self.ssid, to_native(err)))
if self.wait_for_initialization:
self.module.log("Waiting for expansion operation to complete.")
self.wait_for_volume_action()
self.module.log("Volume storage capacities have been expanded.") self.module.log("Volume storage capacities have been expanded.")
def delete_volume(self): def delete_volume(self):
@ -787,6 +797,10 @@ class NetAppESeriesVolume(NetAppESeriesModule):
self.expand_volume() self.expand_volume()
msg = msg[:-1] + " and was expanded." if msg else "Volume [%s] was expanded." msg = msg[:-1] + " and was expanded." if msg else "Volume [%s] was expanded."
if self.wait_for_initialization:
self.module.log("Waiting for volume operation to complete.")
self.wait_for_volume_action(timeout=self.initialization_timeout)
elif self.state == 'absent': elif self.state == 'absent':
self.delete_volume() self.delete_volume()
msg = "Volume [%s] has been deleted." msg = "Volume [%s] has been deleted."

@ -0,0 +1,10 @@
# This test is not enabled by default, but can be utilized by defining required variables in integration_config.yml
# Example integration_config.yml:
# ---
#netapp_e_api_host: 192.168.1.1
#netapp_e_api_username: admin
#netapp_e_api_password: myPass
#netapp_e_ssid: 1
unsupported
netapp/eseries

@ -0,0 +1,776 @@
# Test code for the netapp_e_iscsi_interface module
# (c) 2018, NetApp, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- name: NetApp Test ASUP module
fail:
msg: 'Please define netapp_e_api_username, netapp_e_api_password, netapp_e_api_host, and netapp_e_ssid.'
when: netapp_e_api_username is undefined or netapp_e_api_password is undefined
or netapp_e_api_host is undefined or netapp_e_ssid is undefined
vars:
credentials: &creds
api_url: "https://{{ netapp_e_api_host }}:8443/devmgr/v2"
api_username: "{{ netapp_e_api_username }}"
api_password: "{{ netapp_e_api_password }}"
ssid: "{{ netapp_e_ssid }}"
validate_certs: no
- set_fact:
credentials: *creds
# test setup
- name: Delete raid 0 storage pool
netapp_e_storagepool:
<<: *creds
state: absent
name: "{{ item }}"
loop:
- storage_pool
- storage_pool2
- storage_pool3
# Thick volume testing: create, delete, expand, change properties (read/write cache), expand and change properties,
- name: Create raid 0 storage pool
netapp_e_storagepool:
<<: *creds
state: present
name: storage_pool
criteria_min_usable_capacity: 5
criteria_size_unit: tb
erase_secured_drives: yes
raid_level: raid0
- name: Delete volume in raid 0 storage pool
netapp_e_volume:
<<: *creds
state: absent
name: volume
- name: Create volume in raid 0 storage pool
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 100
size_unit: gb
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '107374182400' and item.segmentSize == 131072}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- name: Re-execute volume creation in raid 0 storage pool
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 100
size_unit: gb
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ not results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '107374182400' and item.segmentSize == 131072}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- name: Update volume size
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 200
size_unit: gb
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '214748364800' and item.segmentSize == 131072}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- pause: seconds=15
- name: Update volume properties
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 200
size_unit: gb
write_cache_enable: true
read_cache_enable: false
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '214748364800' and item.segmentSize == 131072 and
not item.cacheSettings.readCacheEnable and item.cacheSettings.writeCacheEnable}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- name: Update volume properties and expand storage capabilities
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 300
size_unit: gb
write_cache_enable: false
read_cache_enable: true
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '322122547200' and item.segmentSize == 131072 and
item.cacheSettings.readCacheEnable and not item.cacheSettings.writeCacheEnable}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
# Workload tagging testing: create, utilize existing (name only, name with same attributes), modify attributes
- name: Add workload tag (change, new workload tag)
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 300
size_unit: gb
write_cache_enable: false
read_cache_enable: true
workload_name: volume_tag
metadata:
volume_tag_key: volume_tag_value
register: results
- pause: seconds=15
- name: Validate volume workload changes
uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '322122547200' and item.segmentSize == 131072 and
item.cacheSettings.readCacheEnable and not item.cacheSettings.writeCacheEnable and
{'key': 'volumeTypeId', 'value': 'volume'} in item.metadata }}"
msg: "Failed to modify volume metadata!"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: workload_tags
- assert:
that: "{{ item.name == 'volume_tag' and
{'key': 'volume_tag_key', 'value': 'volume_tag_value'} in item.workloadAttributes }}"
msg: "Workload tag failed to be created!"
loop: "{{ lookup('list', volume_tag_id, wantList=True) }}"
vars:
volume_tag_id: "{{ workload_tags | json_query('json[?name==`volume_tag`]') }}"
- name: Repeat add workload tag (no change)
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 300
size_unit: gb
write_cache_enable: false
read_cache_enable: true
workload_name: volume_tag
metadata:
volume_tag_key: volume_tag_value
register: results
- pause: seconds=15
- name: Validate volume workload changes
uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ not results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '322122547200' and item.segmentSize == 131072 and
item.cacheSettings.readCacheEnable and not item.cacheSettings.writeCacheEnable and
{'key': 'volumeTypeId', 'value': 'volume'} in item.metadata }}"
msg: "Failed to not modify volume metadata!"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: workload_tags
- assert:
that: "{{ item.name == 'volume_tag' and
{'key': 'volume_tag_key', 'value': 'volume_tag_value'} in item.workloadAttributes }}"
msg: "Workload tag failed not to be changed"
loop: "{{ lookup('list', volume_tag_id, wantList=True) }}"
vars:
volume_tag_id: "{{ workload_tags | json_query('json[?name==`volume_tag`]') }}"
- name: Workload tag (no change, just using workload_name)
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 300
size_unit: gb
write_cache_enable: false
read_cache_enable: true
workload_name: volume_tag
register: results
- pause: seconds=15
- name: Validate volume workload changes
uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ not results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '322122547200' and item.segmentSize == 131072 and
item.cacheSettings.readCacheEnable and not item.cacheSettings.writeCacheEnable and
{'key': 'volumeTypeId', 'value': 'volume'} in item.metadata }}"
msg: "Failed to not modify volume metadata!"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: workload_tags
- assert:
that: "{{ item.name == 'volume_tag' and
{'key': 'volume_tag_key', 'value': 'volume_tag_value'} in item.workloadAttributes }}"
msg: "Workload tag failed to not be modified!"
loop: "{{ lookup('list', volume_tag_id, wantList=True) }}"
vars:
volume_tag_id: "{{ workload_tags | json_query('json[?name==`volume_tag`]') }}"
- name: Add workload tag (change, new attributes)
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 300
size_unit: gb
write_cache_enable: false
read_cache_enable: true
workload_name: volume_tag
metadata:
volume_tag_key2: volume_tag_value2
register: results
- pause: seconds=15
- name: Validate volume workload changes
uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '322122547200' and item.segmentSize == 131072 and
item.cacheSettings.readCacheEnable and not item.cacheSettings.writeCacheEnable and
{'key': 'volumeTypeId', 'value': 'volume'} in item.metadata }}"
msg: "Failed to not modify volume metadata!"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: workload_tags
- assert:
that: "{{ item.name == 'volume_tag' and
{'key': 'volume_tag_key2', 'value': 'volume_tag_value2'} in item.workloadAttributes }}"
msg: "Workload tag failed to be updated!"
loop: "{{ lookup('list', volume_tag_id, wantList=True) }}"
vars:
volume_tag_id: "{{ workload_tags | json_query('json[?name==`volume_tag`]') }}"
- name: Remove workload tag from volume (change)
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool
size: 300
size_unit: gb
write_cache_enable: false
read_cache_enable: true
register: results
- pause: seconds=15
- name: Validate volume workload changes
uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '322122547200' and item.segmentSize == 131072 and
item.cacheSettings.readCacheEnable and not item.cacheSettings.writeCacheEnable and
item.metadata == []}}"
msg: "Failed to not modify volume metadata!"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: workload_tags
- assert:
that: "{{ item.name == 'volume_tag' and
{'key': 'volume_tag_key2', 'value': 'volume_tag_value2'} in item.workloadAttributes }}"
msg: "Workload tag failed to be updated!"
loop: "{{ lookup('list', volume_tag_id, wantList=True) }}"
vars:
volume_tag_id: "{{ workload_tags | json_query('json[?name==`volume_tag`]') }}"
- name: Delete workload tag
uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: workload_tags
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/workloads/{{ item }}"
method: DELETE
status_code: 204
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
loop: "{{ lookup('list', volume_tag_id, wantList=True) }}"
vars:
volume_tag_id: "{{ workload_tags | json_query('json[?name==`volume_tag`].id') }}"
- name: Delete raid 0 storage pool
netapp_e_storagepool:
<<: *creds
state: absent
name: storage_pool
# *** Thin volume testing (May not work with simulator) ***
- name: Create dynamic disk pool
netapp_e_storagepool:
<<: *creds
state: present
name: storage_pool
criteria_min_usable_capacity: 2
criteria_size_unit: tb
- name: Create thin volume
netapp_e_volume:
<<: *creds
state: present
name: thin_volume
storage_pool_name: storage_pool
size: 131072
size_unit: gb
thin_provision: true
thin_volume_repo_size: 32
thin_volume_max_repo_size: 1024
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'thin_volume' and item.thinProvisioned and
item.capacity == '140737488355328' and item.initialProvisionedCapacity == '34359738368' and
item.provisionedCapacityQuota == '1099511627776' and item.expansionPolicy == 'automatic' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume`]') }}"
- name: (Rerun) Create thin volume
netapp_e_volume:
<<: *creds
state: present
name: thin_volume
storage_pool_name: storage_pool
size: 131072
size_unit: gb
thin_provision: true
thin_volume_repo_size: 32
thin_volume_max_repo_size: 1024
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ not results.changed and item.name == 'thin_volume' and item.thinProvisioned and
item.capacity == '140737488355328' and item.initialProvisionedCapacity == '34359738368' and
item.provisionedCapacityQuota == '1099511627776' and item.expansionPolicy == 'automatic' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume`]') }}"
- name: Expand thin volume's virtual size
netapp_e_volume:
<<: *creds
state: present
name: thin_volume
storage_pool_name: storage_pool
size: 262144
size_unit: gb
thin_provision: true
thin_volume_repo_size: 32
thin_volume_max_repo_size: 1024
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'thin_volume' and item.thinProvisioned and
item.capacity == '281474976710656' and item.initialProvisionedCapacity == '34359738368' and
item.provisionedCapacityQuota == '1099511627776' and item.expansionPolicy == 'automatic' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume`]') }}"
- name: Expand thin volume's maximum repository size
netapp_e_volume:
<<: *creds
state: present
name: thin_volume
storage_pool_name: storage_pool
size: 262144
size_unit: gb
thin_provision: true
thin_volume_repo_size: 32
thin_volume_max_repo_size: 2048
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'thin_volume' and item.thinProvisioned and
item.capacity == '281474976710656' and item.initialProvisionedCapacity == '34359738368' and
item.provisionedCapacityQuota == '2199023255552' and item.expansionPolicy == 'automatic' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume`]') }}"
- name: Create dynamic disk pool
netapp_e_storagepool:
<<: *creds
state: present
name: storage_pool2
criteria_min_usable_capacity: 2
criteria_size_unit: tb
- pause: seconds=15
- name: Create second thin volume with manual expansion policy
netapp_e_volume:
<<: *creds
state: present
name: thin_volume2
storage_pool_name: storage_pool2
size_unit: gb
size: 131072
thin_provision: true
thin_volume_repo_size: 32
thin_volume_max_repo_size: 32
thin_volume_expansion_policy: manual
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'thin_volume2' and item.thinProvisioned and
item.capacity == '140737488355328' and item.initialProvisionedCapacity == '34359738368' and
item.currentProvisionedCapacity == '34359738368' and item.expansionPolicy == 'manual' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume2`]') }}"
- name: Create second thin volume with manual expansion policy
netapp_e_volume:
<<: *creds
state: present
name: thin_volume2
storage_pool_name: storage_pool2
size_unit: gb
size: 131072
thin_provision: true
thin_volume_repo_size: 288
thin_volume_max_repo_size: 288
thin_volume_expansion_policy: manual
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'thin_volume2' and item.thinProvisioned and
item.capacity == '140737488355328' and item.initialProvisionedCapacity == '34359738368' and
item.currentProvisionedCapacity == '309237645312' and item.expansionPolicy == 'manual' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume2`]') }}"
- name: Modify second thin volume to use automatic expansion policy
netapp_e_volume:
<<: *creds
state: present
name: thin_volume2
storage_pool_name: storage_pool2
size_unit: gb
size: 131072
thin_provision: true
thin_volume_repo_size: 288
thin_volume_max_repo_size: 288
thin_volume_expansion_policy: automatic
register: results
- pause: seconds=15
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/thin-volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'thin_volume2' and item.thinProvisioned and
item.capacity == '140737488355328' and item.initialProvisionedCapacity == '34359738368' and
item.currentProvisionedCapacity == '309237645312' and item.expansionPolicy == 'automatic' }}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`thin_volume2`]') }}"
- name: Delete raid 0 storage pool
netapp_e_storagepool:
<<: *creds
state: absent
name: "{{ item }}"
loop:
- storage_pool
- storage_pool2
- name: Create raid 0 storage pool
netapp_e_storagepool:
<<: *creds
state: present
name: storage_pool
criteria_min_usable_capacity: 5
criteria_size_unit: tb
erase_secured_drives: yes
raid_level: raid0
# Thick volume expansion testing: wait and don't wait for operation to complete
- name: Create raid 6 storage pool
netapp_e_storagepool:
<<: *creds
state: present
name: storage_pool3
criteria_min_usable_capacity: 5
criteria_size_unit: tb
erase_secured_drives: yes
raid_level: raid6
- name: Delete volume in raid 6 storage pool
netapp_e_volume:
<<: *creds
state: absent
name: volume
- name: Create volume in raid 0 storage pool for expansion testing
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool3
size: 1
size_unit: gb
register: results
- pause: seconds=10
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '1073741824' and item.segmentSize == 131072}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- name: Modify volume in raid 0 storage pool and wait for expansion testing
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool3
size: 10
size_unit: gb
wait_for_initialization: True
register: results
- pause: seconds=10
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes/{{ volume[0]['id'] }}/expand"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: expansion_state
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '10737418240' and item.segmentSize == 131072 and
expansion_state['json']['action'] == 'none'}}"
msg: "Volume expansion test failed."
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- name: Modify volume in raid 0 storage pool and don't wait for expansion testing
netapp_e_volume:
<<: *creds
state: present
name: volume
storage_pool_name: storage_pool3
size: 100
size_unit: gb
wait_for_initialization: False
register: results
- pause: seconds=10
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: current
- uri:
url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/volumes/{{ volume[0]['id'] }}/expand"
user: "{{ credentials.api_username }}"
password: "{{ credentials.api_password }}"
validate_certs: no
register: expansion_state
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- assert:
that: "{{ results.changed and item.name == 'volume' and not item.thinProvisioned and
item.capacity == '107374182400' and item.segmentSize == 131072 and expansion_state['json']['action'] != 'none'}}"
msg: "Failed to create volume"
loop: "{{ lookup('list', volume, wantList=True) }}"
vars:
volume: "{{ current | json_query('json[?name==`volume`]') }}"
- name: Delete raid 0 storage pool
netapp_e_storagepool:
<<: *creds
state: absent
name: "{{ item }}"
loop:
- storage_pool3

@ -7383,7 +7383,6 @@ test/units/modules/storage/netapp/test_netapp_e_iscsi_target.py future-import-bo
test/units/modules/storage/netapp/test_netapp_e_ldap.py future-import-boilerplate test/units/modules/storage/netapp/test_netapp_e_ldap.py future-import-boilerplate
test/units/modules/storage/netapp/test_netapp_e_mgmt_interface.py future-import-boilerplate test/units/modules/storage/netapp/test_netapp_e_mgmt_interface.py future-import-boilerplate
test/units/modules/storage/netapp/test_netapp_e_syslog.py future-import-boilerplate test/units/modules/storage/netapp/test_netapp_e_syslog.py future-import-boilerplate
test/units/modules/storage/netapp/test_netapp_e_volume.py future-import-boilerplate
test/units/modules/system/interfaces_file/test_interfaces_file.py future-import-boilerplate test/units/modules/system/interfaces_file/test_interfaces_file.py future-import-boilerplate
test/units/modules/system/interfaces_file/test_interfaces_file.py metaclass-boilerplate test/units/modules/system/interfaces_file/test_interfaces_file.py metaclass-boilerplate
test/units/modules/system/interfaces_file/test_interfaces_file.py pylint:blacklisted-name test/units/modules/system/interfaces_file/test_interfaces_file.py pylint:blacklisted-name

@ -1,6 +1,8 @@
# coding=utf-8 # coding=utf-8
# (c) 2018, NetApp Inc. # (c) 2018, NetApp Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
try: try:
from unittest import mock from unittest import mock
@ -11,8 +13,6 @@ from ansible.module_utils.netapp import NetAppESeriesModule
from ansible.modules.storage.netapp.netapp_e_volume import NetAppESeriesVolume from ansible.modules.storage.netapp.netapp_e_volume import NetAppESeriesVolume
from units.modules.utils import AnsibleFailJson, ModuleTestCase, set_module_args from units.modules.utils import AnsibleFailJson, ModuleTestCase, set_module_args
__metaclass__ = type
class NetAppESeriesVolumeTest(ModuleTestCase): class NetAppESeriesVolumeTest(ModuleTestCase):
REQUIRED_PARAMS = {"api_username": "username", REQUIRED_PARAMS = {"api_username": "username",
@ -158,7 +158,40 @@ class NetAppESeriesVolumeTest(ModuleTestCase):
"diskPool": False, "diskPool": False,
"id": "04000000600A098000A4B28D00000FBD5C2F7F19", "id": "04000000600A098000A4B28D00000FBD5C2F7F19",
"name": "database_storage_pool"}] "name": "database_storage_pool"}]
REQUEST_FUNC = "ansible.modules.storage.netapp.netapp_e_volume.NetAppESeriesVolume.request"
GET_LONG_LIVED_OPERATION_RESPONSE = [
{"returnCode": "ok",
"longLivedOpsProgress": [
{"volAction": "initializing", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B9D1000037315D494C6F", "pending": False, "percentComplete": 1, "timeToCompletion": 20},
"format": None, "volCreation": None, "volDeletion": None},
{"volAction": "initializing", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B28D00003D2C5D494C87", "pending": False, "percentComplete": 0, "timeToCompletion": 18},
"volCreation": None, "volDeletion": None}]},
{"returnCode": "ok",
"longLivedOpsProgress": [
{"volAction": "complete", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B9D1000037315D494C6F", "pending": False, "percentComplete": 1, "timeToCompletion": 20},
"format": None, "volCreation": None, "volDeletion": None},
{"volAction": "initializing", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B28D00003D2C5D494C87", "pending": False, "percentComplete": 0, "timeToCompletion": 18},
"volCreation": None, "volDeletion": None}]},
{"returnCode": "ok",
"longLivedOpsProgress": [
{"volAction": "initializing", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B9D1000037315D494C6F", "pending": False, "percentComplete": 1, "timeToCompletion": 20},
"format": None, "volCreation": None, "volDeletion": None},
{"volAction": "complete", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B28D00003D2C5D494C87", "pending": False, "percentComplete": 0, "timeToCompletion": 18},
"volCreation": None, "volDeletion": None}]},
{"returnCode": "ok",
"longLivedOpsProgress": [
{"volAction": "complete", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B9D1000037315D494C6F", "pending": False, "percentComplete": 1, "timeToCompletion": 20},
"format": None, "volCreation": None, "volDeletion": None},
{"volAction": "complete", "reconstruct": None, "volExpansion": None, "volAndCapExpansion": None,
"init": {"volumeRef": "02000000600A098000A4B28D00003D2C5D494C87", "pending": False, "percentComplete": 0, "timeToCompletion": 18},
"volCreation": None, "volDeletion": None}]}]
WORKLOAD_GET_RESPONSE = [{"id": "4200000001000000000000000000000000000000", "name": "general_workload_1", WORKLOAD_GET_RESPONSE = [{"id": "4200000001000000000000000000000000000000", "name": "general_workload_1",
"workloadAttributes": [{"key": "profileId", "value": "Other_1"}]}, "workloadAttributes": [{"key": "profileId", "value": "Other_1"}]},
@ -177,6 +210,10 @@ class NetAppESeriesVolumeTest(ModuleTestCase):
{"key": "location", "value": "global"}, {"key": "location", "value": "global"},
{"key": "profileId", "value": "ansible_workload_4"}]}] {"key": "profileId", "value": "ansible_workload_4"}]}]
REQUEST_FUNC = "ansible.modules.storage.netapp.netapp_e_volume.NetAppESeriesVolume.request"
GET_VOLUME_FUNC = "ansible.modules.storage.netapp.netapp_e_volume.NetAppESeriesVolume.get_volume"
SLEEP_FUNC = "ansible.modules.storage.netapp.netapp_e_volume.sleep"
def _set_args(self, args=None): def _set_args(self, args=None):
module_args = self.REQUIRED_PARAMS.copy() module_args = self.REQUIRED_PARAMS.copy()
if args is not None: if args is not None:
@ -268,6 +305,67 @@ class NetAppESeriesVolumeTest(ModuleTestCase):
volume_object = NetAppESeriesVolume() volume_object = NetAppESeriesVolume()
volume_object.get_volume() volume_object.get_volume()
def tests_wait_for_volume_availability_pass(self):
"""Ensure wait_for_volume_availability completes as expected."""
self._set_args({"state": "present", "name": "NewVolume", "storage_pool_name": "employee_data_storage_pool", "size": 100,
"wait_for_initialization": True})
volume_object = NetAppESeriesVolume()
with mock.patch(self.SLEEP_FUNC, return_value=None):
with mock.patch(self.GET_VOLUME_FUNC, side_effect=[False, False, True]):
volume_object.wait_for_volume_availability()
def tests_wait_for_volume_availability_fail(self):
"""Ensure wait_for_volume_availability throws the expected exceptions."""
self._set_args({"state": "present", "name": "NewVolume", "storage_pool_name": "employee_data_storage_pool", "size": 100,
"wait_for_initialization": True})
volume_object = NetAppESeriesVolume()
volume_object.get_volume = lambda: False
with self.assertRaisesRegexp(AnsibleFailJson, "Timed out waiting for the volume"):
with mock.patch(self.SLEEP_FUNC, return_value=None):
volume_object.wait_for_volume_availability()
def tests_wait_for_volume_action_pass(self):
"""Ensure wait_for_volume_action completes as expected."""
self._set_args({"state": "present", "name": "NewVolume", "storage_pool_name": "employee_data_storage_pool", "size": 100,
"wait_for_initialization": True})
volume_object = NetAppESeriesVolume()
volume_object.volume_detail = {"id": "02000000600A098000A4B9D1000037315D494C6F",
"storageVolumeRef": "02000000600A098000A4B9D1000037315DXXXXXX"}
with mock.patch(self.SLEEP_FUNC, return_value=None):
with mock.patch(self.REQUEST_FUNC, side_effect=[(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[0]),
(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[1]),
(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[2]),
(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[3])]):
volume_object.wait_for_volume_action()
self._set_args({"state": "present", "name": "NewVolume", "storage_pool_name": "employee_data_storage_pool", "size": 100,
"wait_for_initialization": True})
volume_object = NetAppESeriesVolume()
volume_object.volume_detail = {"id": "02000000600A098000A4B9D1000037315DXXXXXX",
"storageVolumeRef": "02000000600A098000A4B9D1000037315D494C6F"}
with mock.patch(self.SLEEP_FUNC, return_value=None):
with mock.patch(self.REQUEST_FUNC, side_effect=[(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[0]),
(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[1]),
(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[2]),
(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[3])]):
volume_object.wait_for_volume_action()
def tests_wait_for_volume_action_fail(self):
"""Ensure wait_for_volume_action throws the expected exceptions."""
self._set_args({"state": "present", "name": "NewVolume", "storage_pool_name": "employee_data_storage_pool", "size": 100,
"wait_for_initialization": True})
volume_object = NetAppESeriesVolume()
volume_object.volume_detail = {"id": "02000000600A098000A4B9D1000037315DXXXXXX",
"storageVolumeRef": "02000000600A098000A4B9D1000037315D494C6F"}
with mock.patch(self.SLEEP_FUNC, return_value=None):
with self.assertRaisesRegexp(AnsibleFailJson, "Failed to get volume expansion progress."):
with mock.patch(self.REQUEST_FUNC, return_value=Exception()):
volume_object.wait_for_volume_action()
with self.assertRaisesRegexp(AnsibleFailJson, "Expansion action failed to complete."):
with mock.patch(self.REQUEST_FUNC, return_value=(200, self.GET_LONG_LIVED_OPERATION_RESPONSE[0])):
volume_object.wait_for_volume_action(timeout=300)
def test_get_storage_pool_pass(self): def test_get_storage_pool_pass(self):
"""Evaluate the get_storage_pool method.""" """Evaluate the get_storage_pool method."""
with mock.patch(self.REQUEST_FUNC, return_value=(200, self.STORAGE_POOL_GET_RESPONSE)): with mock.patch(self.REQUEST_FUNC, return_value=(200, self.STORAGE_POOL_GET_RESPONSE)):

Loading…
Cancel
Save