Add IOPs bandwidth support to volumes (#61577)
This commit is contained in:
parent
3bd69a47ec
commit
cd24d57636
2 changed files with 193 additions and 106 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- purefa_volume - Change I(qos) parameter to I(bw_iops), but retain I(qos) as an alias for backwards compatability (https://github.com/ansible/ansible/pull/61577).
|
|
@ -50,14 +50,23 @@ options:
|
||||||
description:
|
description:
|
||||||
- Volume size in M, G, T or P units.
|
- Volume size in M, G, T or P units.
|
||||||
type: str
|
type: str
|
||||||
qos:
|
bw_qos:
|
||||||
description:
|
description:
|
||||||
- Bandwidth limit for volume in M or G units.
|
- Bandwidth limit for volume in M or G units.
|
||||||
M will set MB/s
|
M will set MB/s
|
||||||
G will set GB/s
|
G will set GB/s
|
||||||
To clear an existing Qos setting using 0 (zero)
|
To clear an existing QoS setting use 0 (zero)
|
||||||
version_added: '2.8'
|
version_added: '2.8'
|
||||||
type: str
|
type: str
|
||||||
|
aliases: [ qos ]
|
||||||
|
iops_qos:
|
||||||
|
description:
|
||||||
|
- IOPs limit for volume - use value or K or M
|
||||||
|
K will mean 1000
|
||||||
|
M will mean 1000000
|
||||||
|
To clear an existing IOPs setting use 0 (zero)
|
||||||
|
version_added: '2.9'
|
||||||
|
type: str
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- purestorage.fa
|
- purestorage.fa
|
||||||
'''
|
'''
|
||||||
|
@ -67,7 +76,8 @@ EXAMPLES = r'''
|
||||||
purefa_volume:
|
purefa_volume:
|
||||||
name: foo
|
name: foo
|
||||||
size: 1T
|
size: 1T
|
||||||
qos: 58M
|
bw_qos: 58M
|
||||||
|
iops_qos: 23K
|
||||||
fa_url: 10.10.10.2
|
fa_url: 10.10.10.2
|
||||||
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
||||||
state: present
|
state: present
|
||||||
|
@ -108,7 +118,8 @@ EXAMPLES = r'''
|
||||||
- name: Clear volume QoS from volume foo
|
- name: Clear volume QoS from volume foo
|
||||||
purefa_volume:
|
purefa_volume:
|
||||||
name: foo
|
name: foo
|
||||||
qos: 0
|
bw_qos: 0
|
||||||
|
iops_qos: 0
|
||||||
fa_url: 10.10.10.2
|
fa_url: 10.10.10.2
|
||||||
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
||||||
state: present
|
state: present
|
||||||
|
@ -138,6 +149,12 @@ volume:
|
||||||
size:
|
size:
|
||||||
description: Volume size in bytes
|
description: Volume size in bytes
|
||||||
type: int
|
type: int
|
||||||
|
bandwidth_limit:
|
||||||
|
description: Volume bandwidth limit in bytes/sec
|
||||||
|
type: int
|
||||||
|
iops_limit:
|
||||||
|
description: Volume IOPs limit
|
||||||
|
type: int
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
@ -147,6 +164,7 @@ from ansible.module_utils.pure import get_system, purefa_argument_spec
|
||||||
QOS_API_VERSION = "1.14"
|
QOS_API_VERSION = "1.14"
|
||||||
VGROUPS_API_VERSION = "1.13"
|
VGROUPS_API_VERSION = "1.13"
|
||||||
POD_API_VERSION = "1.13"
|
POD_API_VERSION = "1.13"
|
||||||
|
IOPS_API_VERSION = "1.17"
|
||||||
|
|
||||||
|
|
||||||
def human_to_bytes(size):
|
def human_to_bytes(size):
|
||||||
|
@ -166,6 +184,8 @@ def human_to_bytes(size):
|
||||||
bytes *= 1073741824
|
bytes *= 1073741824
|
||||||
elif unit == 'M':
|
elif unit == 'M':
|
||||||
bytes *= 1048576
|
bytes *= 1048576
|
||||||
|
elif unit == 'K':
|
||||||
|
bytes *= 1024
|
||||||
else:
|
else:
|
||||||
bytes = 0
|
bytes = 0
|
||||||
else:
|
else:
|
||||||
|
@ -173,6 +193,26 @@ def human_to_bytes(size):
|
||||||
return bytes
|
return bytes
|
||||||
|
|
||||||
|
|
||||||
|
def human_to_real(iops):
|
||||||
|
"""Given a human-readable IOPs string (e.g. 2K, 30M),
|
||||||
|
return the real number. Will return 0 if the argument has
|
||||||
|
unexpected form.
|
||||||
|
"""
|
||||||
|
digit = iops[:-1]
|
||||||
|
unit = iops[-1]
|
||||||
|
if digit.isdigit():
|
||||||
|
digit = int(digit)
|
||||||
|
if unit == 'M':
|
||||||
|
digit *= 1000000
|
||||||
|
elif unit == 'K':
|
||||||
|
digit *= 1000
|
||||||
|
else:
|
||||||
|
digit = 0
|
||||||
|
else:
|
||||||
|
digit = 0
|
||||||
|
return digit
|
||||||
|
|
||||||
|
|
||||||
def get_volume(module, array):
|
def get_volume(module, array):
|
||||||
"""Return Volume or None"""
|
"""Return Volume or None"""
|
||||||
try:
|
try:
|
||||||
|
@ -184,10 +224,7 @@ def get_volume(module, array):
|
||||||
def get_destroyed_volume(module, array):
|
def get_destroyed_volume(module, array):
|
||||||
"""Return Destroyed Volume or None"""
|
"""Return Destroyed Volume or None"""
|
||||||
try:
|
try:
|
||||||
if array.get_volume(module.params['name'], pending=True)['time_remaining'] != '':
|
return bool(array.get_volume(module.params['name'], pending=True)['time_remaining'] != '')
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -240,127 +277,173 @@ def check_pod(module, array):
|
||||||
|
|
||||||
def create_volume(module, array):
|
def create_volume(module, array):
|
||||||
"""Create Volume"""
|
"""Create Volume"""
|
||||||
changed = False
|
changed = True
|
||||||
if "/" in module.params['name'] and not check_vgroup(module, array):
|
if not module.check_mode:
|
||||||
module.fail_json(msg="Failed to create volume {0}. Volume Group does not exist.".format(module.params["name"]))
|
if "/" in module.params['name'] and not check_vgroup(module, array):
|
||||||
if "::" in module.params['name'] and not check_pod(module, array):
|
module.fail_json(msg="Failed to create volume {0}. Volume Group does not exist.".format(module.params["name"]))
|
||||||
module.fail_json(msg="Failed to create volume {0}. Poid does not exist".format(module.params["name"]))
|
if "::" in module.params['name'] and not check_pod(module, array):
|
||||||
volfact = []
|
module.fail_json(msg="Failed to create volume {0}. Poid does not exist".format(module.params["name"]))
|
||||||
api_version = array._list_available_rest_versions()
|
volfact = []
|
||||||
if module.params['qos'] and QOS_API_VERSION in api_version:
|
api_version = array._list_available_rest_versions()
|
||||||
if 549755813888 >= int(human_to_bytes(module.params['qos'])) >= 1048576:
|
if module.params['bw_qos'] or module.params['iops_qos']:
|
||||||
|
if module.params['bw_qos'] and QOS_API_VERSION in api_version or module.params['iops_qos'] and IOPS_API_VERSION in api_version:
|
||||||
|
if module.params['bw_qos'] and not module.params['iops_qos']:
|
||||||
|
if 549755813888 >= int(human_to_bytes(module.params['bw_qos'])) >= 1048576:
|
||||||
|
try:
|
||||||
|
volfact = array.create_volume(module.params['name'],
|
||||||
|
module.params['size'],
|
||||||
|
bandwidth_limit=module.params['bw_qos'])
|
||||||
|
except Exception:
|
||||||
|
module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name']))
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Bandwidth QoS value {0} out of range.'.format(module.params['bw_qos']))
|
||||||
|
elif module.params['iops_qos'] and not module.params['bw_qos']:
|
||||||
|
if 100000000 >= int(human_to_real(module.params['iops_qos'])) >= 100:
|
||||||
|
try:
|
||||||
|
volfact = array.create_volume(module.params['name'],
|
||||||
|
module.params['size'],
|
||||||
|
iops_limit=module.params['iops_qos'])
|
||||||
|
except Exception:
|
||||||
|
module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name']))
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='IOPs QoS value {0} out of range.'.format(module.params['iops_qos']))
|
||||||
|
else:
|
||||||
|
bw_qos_size = int(human_to_bytes(module.params['bw_qos']))
|
||||||
|
if 100000000 >= int(human_to_real(module.params['iops_qos'])) >= 100 and 549755813888 >= bw_qos_size >= 1048576:
|
||||||
|
try:
|
||||||
|
volfact = array.create_volume(module.params['name'],
|
||||||
|
module.params['size'],
|
||||||
|
iops_limit=module.params['iops_qos'],
|
||||||
|
bandwidth_limit=module.params['bw_qos'])
|
||||||
|
except Exception:
|
||||||
|
module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name']))
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='IOPs or Bandwidth QoS value out of range.')
|
||||||
|
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
volfact = array.create_volume(module.params['name'],
|
volfact = array.create_volume(module.params['name'], module.params['size'])
|
||||||
module.params['size'],
|
|
||||||
bandwidth_limit=module.params['qos'])
|
|
||||||
changed = True
|
|
||||||
except Exception:
|
except Exception:
|
||||||
module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name']))
|
module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name']))
|
||||||
else:
|
|
||||||
module.fail_json(msg='QoS value {0} out of range.'.format(module.params['qos']))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
volfact = array.create_volume(module.params['name'], module.params['size'])
|
|
||||||
changed = True
|
|
||||||
except Exception:
|
|
||||||
module.fail_json(msg='Volume {0} creation failed.'.format(module.params['name']))
|
|
||||||
|
|
||||||
module.exit_json(changed=changed, volume=volfact)
|
module.exit_json(changed=changed, volume=volfact)
|
||||||
|
|
||||||
|
|
||||||
def copy_from_volume(module, array):
|
def copy_from_volume(module, array):
|
||||||
"""Create Volume Clone"""
|
"""Create Volume Clone"""
|
||||||
changed = False
|
changed = True
|
||||||
volfact = []
|
if not module.check_mode:
|
||||||
tgt = get_target(module, array)
|
volfact = []
|
||||||
|
tgt = get_target(module, array)
|
||||||
|
|
||||||
if tgt is None:
|
if tgt is None:
|
||||||
try:
|
try:
|
||||||
volfact = array.copy_volume(module.params['name'],
|
volfact = array.copy_volume(module.params['name'],
|
||||||
module.params['target'])
|
module.params['target'])
|
||||||
changed = True
|
except Exception:
|
||||||
except Exception:
|
module.fail_json(msg='Copy volume {0} to volume {1} failed.'.format(module.params['name'],
|
||||||
module.fail_json(msg='Copy volume {0} to volume {1} failed.'.format(module.params['name'],
|
module.params['target']))
|
||||||
module.params['target']))
|
elif tgt is not None and module.params['overwrite']:
|
||||||
elif tgt is not None and module.params['overwrite']:
|
try:
|
||||||
try:
|
volfact = array.copy_volume(module.params['name'],
|
||||||
volfact = array.copy_volume(module.params['name'],
|
module.params['target'],
|
||||||
module.params['target'],
|
overwrite=module.params['overwrite'])
|
||||||
overwrite=module.params['overwrite'])
|
except Exception:
|
||||||
changed = True
|
module.fail_json(msg='Copy volume {0} to volume {1} failed.'.format(module.params['name'],
|
||||||
except Exception:
|
module.params['target']))
|
||||||
module.fail_json(msg='Copy volume {0} to volume {1} failed.'.format(module.params['name'],
|
|
||||||
module.params['target']))
|
|
||||||
|
|
||||||
module.exit_json(changed=changed, volume=volfact)
|
module.exit_json(changed=changed, volume=volfact)
|
||||||
|
|
||||||
|
|
||||||
def update_volume(module, array):
|
def update_volume(module, array):
|
||||||
"""Update Volume size and/or QoS"""
|
"""Update Volume size and/or QoS"""
|
||||||
changed = False
|
changed = True
|
||||||
volfact = []
|
if not module.check_mode:
|
||||||
api_version = array._list_available_rest_versions()
|
change = False
|
||||||
vol = array.get_volume(module.params['name'])
|
volfact = []
|
||||||
if QOS_API_VERSION in api_version:
|
api_version = array._list_available_rest_versions()
|
||||||
|
vol = array.get_volume(module.params['name'])
|
||||||
vol_qos = array.get_volume(module.params['name'], qos=True)
|
vol_qos = array.get_volume(module.params['name'], qos=True)
|
||||||
if vol_qos['bandwidth_limit'] is None:
|
if QOS_API_VERSION in api_version:
|
||||||
vol_qos['bandwidth_limit'] = 0
|
if vol_qos['bandwidth_limit'] is None:
|
||||||
if module.params['size']:
|
vol_qos['bandwidth_limit'] = 0
|
||||||
if human_to_bytes(module.params['size']) != vol['size']:
|
if IOPS_API_VERSION in api_version:
|
||||||
if human_to_bytes(module.params['size']) > vol['size']:
|
if vol_qos['iops_limit'] is None:
|
||||||
try:
|
vol_qos['iops_limit'] = 0
|
||||||
volfact = array.extend_volume(module.params['name'], module.params['size'])
|
if module.params['size']:
|
||||||
changed = True
|
if human_to_bytes(module.params['size']) != vol['size']:
|
||||||
except Exception:
|
if human_to_bytes(module.params['size']) > vol['size']:
|
||||||
module.fail_json(msg='Volume {0} resize failed.'.format(module.params['name']))
|
try:
|
||||||
if module.params['qos'] and QOS_API_VERSION in api_version:
|
volfact = array.extend_volume(module.params['name'], module.params['size'])
|
||||||
if human_to_bytes(module.params['qos']) != vol_qos['bandwidth_limit']:
|
change = True
|
||||||
if module.params['qos'] == '0':
|
except Exception:
|
||||||
try:
|
module.fail_json(msg='Volume {0} resize failed.'.format(module.params['name']))
|
||||||
volfact = array.set_volume(module.params['name'], bandwidth_limit='')
|
if module.params['bw_qos'] and QOS_API_VERSION in api_version:
|
||||||
changed = True
|
if human_to_bytes(module.params['bw_qos']) != vol_qos['bandwidth_limit']:
|
||||||
except Exception:
|
if module.params['bw_qos'] == '0':
|
||||||
module.fail_json(msg='Volume {0} QoS removal failed.'.format(module.params['name']))
|
try:
|
||||||
elif 549755813888 >= int(human_to_bytes(module.params['qos'])) >= 1048576:
|
volfact = array.set_volume(module.params['name'], bandwidth_limit='')
|
||||||
try:
|
change = True
|
||||||
volfact = array.set_volume(module.params['name'],
|
except Exception:
|
||||||
bandwidth_limit=module.params['qos'])
|
module.fail_json(msg='Volume {0} Bandwidth QoS removal failed.'.format(module.params['name']))
|
||||||
changed = True
|
elif 549755813888 >= int(human_to_bytes(module.params['bw_qos'])) >= 1048576:
|
||||||
except Exception:
|
try:
|
||||||
module.fail_json(msg='Volume {0} QoS change failed.'.format(module.params['name']))
|
volfact = array.set_volume(module.params['name'],
|
||||||
else:
|
bandwidth_limit=module.params['bw_qos'])
|
||||||
module.fail_json(msg='QoS value {0} out of range. Check documentation.'.format(module.params['qos']))
|
change = True
|
||||||
|
except Exception:
|
||||||
|
module.fail_json(msg='Volume {0} Bandwidth QoS change failed.'.format(module.params['name']))
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Bandwidth QoS value {0} out of range.'.format(module.params['bw_qos']))
|
||||||
|
if module.params['iops_qos'] and IOPS_API_VERSION in api_version:
|
||||||
|
if human_to_real(module.params['iops_qos']) != vol_qos['iops_limit']:
|
||||||
|
if module.params['iops_qos'] == '0':
|
||||||
|
try:
|
||||||
|
volfact = array.set_volume(module.params['name'], iops_limit='')
|
||||||
|
change = True
|
||||||
|
except Exception:
|
||||||
|
module.fail_json(msg='Volume {0} IOPs QoS removal failed.'.format(module.params['name']))
|
||||||
|
elif 100000000 >= int(human_to_real(module.params['iops_qos'])) >= 100:
|
||||||
|
try:
|
||||||
|
volfact = array.set_volume(module.params['name'],
|
||||||
|
iops_limit=module.params['iops_qos'])
|
||||||
|
except Exception:
|
||||||
|
module.fail_json(msg='Volume {0} IOPs QoS change failed.'.format(module.params['name']))
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Bandwidth QoS value {0} out of range.'.format(module.params['bw_qos']))
|
||||||
|
|
||||||
module.exit_json(changed=changed, volume=volfact)
|
module.exit_json(changed=change, volume=volfact)
|
||||||
|
|
||||||
|
module.exit_json(changed=changed)
|
||||||
|
|
||||||
|
|
||||||
def delete_volume(module, array):
|
def delete_volume(module, array):
|
||||||
""" Delete Volume"""
|
""" Delete Volume"""
|
||||||
changed = False
|
changed = True
|
||||||
volfact = []
|
if not module.check_mode:
|
||||||
try:
|
volfact = []
|
||||||
array.destroy_volume(module.params['name'])
|
try:
|
||||||
if module.params['eradicate']:
|
array.destroy_volume(module.params['name'])
|
||||||
try:
|
if module.params['eradicate']:
|
||||||
volfact = array.eradicate_volume(module.params['name'])
|
try:
|
||||||
except Exception:
|
volfact = array.eradicate_volume(module.params['name'])
|
||||||
module.fail_json(msg='Eradicate volume {0} failed.'.format(module.params['name']))
|
except Exception:
|
||||||
changed = True
|
module.fail_json(msg='Eradicate volume {0} failed.'.format(module.params['name']))
|
||||||
except Exception:
|
except Exception:
|
||||||
module.fail_json(msg='Delete volume {0} failed.'.format(module.params['name']))
|
module.fail_json(msg='Delete volume {0} failed.'.format(module.params['name']))
|
||||||
module.exit_json(changed=changed, volume=volfact)
|
module.exit_json(changed=changed, volume=volfact)
|
||||||
|
|
||||||
|
|
||||||
def eradicate_volume(module, array):
|
def eradicate_volume(module, array):
|
||||||
""" Eradicate Deleted Volume"""
|
""" Eradicate Deleted Volume"""
|
||||||
changed = False
|
changed = True
|
||||||
volfact = []
|
if not module.check_mode:
|
||||||
if module.params['eradicate']:
|
volfact = []
|
||||||
try:
|
if module.params['eradicate']:
|
||||||
array.eradicate_volume(module.params['name'])
|
try:
|
||||||
changed = True
|
array.eradicate_volume(module.params['name'])
|
||||||
except Exception:
|
except Exception:
|
||||||
module.fail_json(msg='Eradication of volume {0} failed'.format(module.params['name']))
|
module.fail_json(msg='Eradication of volume {0} failed'.format(module.params['name']))
|
||||||
module.exit_json(changed=changed, volume=volfact)
|
module.exit_json(changed=changed, volume=volfact)
|
||||||
|
|
||||||
|
|
||||||
|
@ -372,7 +455,8 @@ def main():
|
||||||
overwrite=dict(type='bool', default=False),
|
overwrite=dict(type='bool', default=False),
|
||||||
eradicate=dict(type='bool', default=False),
|
eradicate=dict(type='bool', default=False),
|
||||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||||
qos=dict(type='str'),
|
bw_qos=dict(type='str', aliases=['qos']),
|
||||||
|
iops_qos=dict(type='str'),
|
||||||
size=dict(type='str'),
|
size=dict(type='str'),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -380,10 +464,11 @@ def main():
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec,
|
module = AnsibleModule(argument_spec,
|
||||||
mutually_exclusive=mutually_exclusive,
|
mutually_exclusive=mutually_exclusive,
|
||||||
supports_check_mode=False)
|
supports_check_mode=True)
|
||||||
|
|
||||||
size = module.params['size']
|
size = module.params['size']
|
||||||
qos = module.params['qos']
|
bw_qos = module.params['bw_qos']
|
||||||
|
iops_qos = module.params['iops_qos']
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
array = get_system(module)
|
array = get_system(module)
|
||||||
volume = get_volume(module, array)
|
volume = get_volume(module, array)
|
||||||
|
@ -393,7 +478,7 @@ def main():
|
||||||
|
|
||||||
if state == 'present' and not volume and size:
|
if state == 'present' and not volume and size:
|
||||||
create_volume(module, array)
|
create_volume(module, array)
|
||||||
elif state == 'present' and volume and (size or qos):
|
elif state == 'present' and volume and (size or bw_qos or iops_qos):
|
||||||
update_volume(module, array)
|
update_volume(module, array)
|
||||||
elif state == 'present' and volume and target:
|
elif state == 'present' and volume and target:
|
||||||
copy_from_volume(module, array)
|
copy_from_volume(module, array)
|
||||||
|
|
Loading…
Reference in a new issue