Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Module: na_ontap_quotas #49783

Merged
merged 8 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
344 changes: 344 additions & 0 deletions lib/ansible/modules/storage/netapp/na_ontap_quotas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
#!/usr/bin/python

# (c) 2018-2019, NetApp, Inc
# 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


ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'certified'}


DOCUMENTATION = '''
module: na_ontap_quotas
short_description: NetApp ONTAP Quotas
extends_documentation_fragment:
- netapp.na_ontap
version_added: '2.8'
author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
description:
- Set/Modify/Delete quota on ONTAP
options:
carchi8py marked this conversation as resolved.
Show resolved Hide resolved
state:
description:
- Whether the specified quota should exist or not.
choices: ['present', 'absent']
default: present
type: str
vserver:
required: true
description:
- Name of the vserver to use.
type: str
volume:
description:
- The name of the volume that the quota resides on.
required: true
type: str
quota_target:
description:
- The quota target of the type specified.
required: true
type: str
qtree:
description:
- Name of the qtree for the quota.
- For user or group rules, it can be the qtree name or "" if no qtree.
- For tree type rules, this field must be "".
default: ""
type: str
type:
description:
- The type of quota rule
choices: ['user', 'group', 'tree']
required: true
type: str
policy:
description:
- Name of the quota policy from which the quota rule should be obtained.
type: str
set_quota_status:
description:
- Whether the specified volume should have quota status on or off.
type: bool
file_limit:
description:
- The number of files that the target can have.
default: '-'
type: str
disk_limit:
description:
- The amount of disk space that is reserved for the target.
default: '-'
type: str
threshold:
description:
- The amount of disk space the target would have to exceed before a message is logged.
default: '-'
type: str
'''

EXAMPLES = """
- name: Add/Set quota
na_ontap_quotas:
state: present
vserver: ansible
volume: ansible
quota_target: /vol/ansible
type: user
policy: ansible
file_limit: 2
disk_limit: 3
set_quota_status: True
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: modify quota
na_ontap_quotas:
state: present
vserver: ansible
volume: ansible
quota_target: /vol/ansible
type: user
policy: ansible
file_limit: 2
disk_limit: 3
threshold: 3
set_quota_status: False
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Delete quota
na_ontap_quotas:
state: absent
vserver: ansible
volume: ansible
quota_target: /vol/ansible
type: user
policy: ansible
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
"""

RETURN = """

"""


import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
from ansible.module_utils.netapp_module import NetAppModule

HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()


class NetAppONTAPQuotas(object):
'''Class with quotas methods'''

def __init__(self):

self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, choices=['present', 'absent'], default='present'),
vserver=dict(required=True, type='str'),
volume=dict(required=True, type='str'),
quota_target=dict(required=True, type='str'),
qtree=dict(required=False, type='str', default=""),
type=dict(required=True, type='str', choices=['user', 'group', 'tree']),
policy=dict(required=False, type='str'),
set_quota_status=dict(required=False, type='bool'),
file_limit=dict(required=False, type='str', default='-'),
disk_limit=dict(required=False, type='str', default='-'),
threshold=dict(required=False, type='str', default='-')
))

self.module = AnsibleModule(
argument_spec=self.argument_spec,
supports_check_mode=True
)

self.na_helper = NetAppModule()
self.parameters = self.na_helper.set_parameters(self.module.params)

if HAS_NETAPP_LIB is False:
self.module.fail_json(
msg="the python NetApp-Lib module is required")
else:
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])

def get_quota_status(self):
"""
Return details about the quota status
:param:
name : volume name
:return: status of the quota. None if not found.
:rtype: dict
"""
quota_status_get = netapp_utils.zapi.NaElement('quota-status')
quota_status_get.translate_struct({
'volume': self.parameters['volume']
})
try:
result = self.server.invoke_successfully(quota_status_get, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error fetching quotas status info: %s' % to_native(error),
exception=traceback.format_exc())
if result:
return result['status']
return None

def get_quotas(self):
"""
Get quota details
:return: name of volume if quota exists, None otherwise
"""
quota_get = netapp_utils.zapi.NaElement('quota-list-entries-iter')
query = {
'query': {
'quota-entry': {
'volume': self.parameters['volume'],
'quota-target': self.parameters['quota_target'],
'quota-type': self.parameters['type']
}
}
}
quota_get.translate_struct(query)
if self.parameters.get('policy'):
quota_get['query']['quota-entry'].add_new_child('policy', self.parameters['policy'])
try:
result = self.server.invoke_successfully(quota_get, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error fetching quotas info: %s' % to_native(error),
exception=traceback.format_exc())
if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
return_values = {'volume': result['attributes-list']['quota-entry']['volume'],
'file_limit': result['attributes-list']['quota-entry']['file-limit'],
'disk_limit': result['attributes-list']['quota-entry']['disk-limit'],
'threshold': result['attributes-list']['quota-entry']['threshold']}
return return_values
return None

def quota_entry_set(self):
"""
Adds a quota entry
"""
options = {'volume': self.parameters['volume'],
'quota-target': self.parameters['quota_target'],
'quota-type': self.parameters['type'],
'qtree': self.parameters['qtree'],
'file-limit': self.parameters['file_limit'],
'disk-limit': self.parameters['disk_limit'],
'threshold': self.parameters['threshold']}
if self.parameters.get('policy'):
options['policy'] = self.parameters['policy']
set_entry = netapp_utils.zapi.NaElement.create_node_with_children(
'quota-set-entry', **options)
try:
self.server.invoke_successfully(set_entry, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error adding/modifying quota entry %s: %s'
% (self.parameters['volume'], to_native(error)),
exception=traceback.format_exc())

def quota_entry_delete(self):
"""
Deletes a quota entry
"""
options = {'volume': self.parameters['volume'],
'quota-target': self.parameters['quota_target'],
'quota-type': self.parameters['type'],
'qtree': self.parameters['qtree']}
set_entry = netapp_utils.zapi.NaElement.create_node_with_children(
'quota-delete-entry', **options)
if self.parameters.get('policy'):
set_entry.add_new_child('policy', self.parameters['policy'])
try:
self.server.invoke_successfully(set_entry, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error deleting quota entry %s: %s'
% (self.parameters['volume'], to_native(error)),
exception=traceback.format_exc())

def quota_entry_modify(self, modify_attrs):
"""
Modifies a quota entry
"""
options = {'volume': self.parameters['volume'],
'quota-target': self.parameters['quota_target'],
'quota-type': self.parameters['type'],
'qtree': self.parameters['qtree']}
options.update(modify_attrs)
if self.parameters.get('policy'):
options['policy'] = str(self.parameters['policy'])
modify_entry = netapp_utils.zapi.NaElement.create_node_with_children(
'quota-modify-entry', **options)
try:
self.server.invoke_successfully(modify_entry, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error modifying quota entry %s: %s'
% (self.parameters['volume'], to_native(error)),
exception=traceback.format_exc())

def on_or_off_quota(self, status):
"""
on or off quota
"""
quota = netapp_utils.zapi.NaElement.create_node_with_children(
status, **{'volume': self.parameters['volume']})
try:
self.server.invoke_successfully(quota,
enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error setting %s for %s: %s'
% (status, self.parameters['volume'], to_native(error)),
exception=traceback.format_exc())

def apply(self):
"""
Apply action to quotas
"""
netapp_utils.ems_log_event("na_ontap_quotas", self.server)
modify_quota_status = None
modify_quota = None
current = self.get_quotas()
if 'set_quota_status' in self.parameters:
quota_status = self.get_quota_status()
if quota_status is not None:
quota_status_action = self.na_helper.get_modified_attributes(
{'set_quota_status': True if quota_status == 'on' else False}, self.parameters)
if quota_status_action:
modify_quota_status = 'quota-on' if quota_status_action['set_quota_status'] else 'quota-off'
cd_action = self.na_helper.get_cd_action(current, self.parameters)
if cd_action is None:
modify_quota = self.na_helper.get_modified_attributes(current, self.parameters)
if self.na_helper.changed:
if self.module.check_mode:
pass
else:
if cd_action == 'create':
self.quota_entry_set()
elif cd_action == 'delete':
self.quota_entry_delete()
elif modify_quota is not None:
for key in modify_quota:
modify_quota[key.replace("_", "-")] = modify_quota.pop(key)
self.quota_entry_modify(modify_quota)
if modify_quota_status is not None:
self.on_or_off_quota(modify_quota_status)
self.module.exit_json(changed=self.na_helper.changed)


def main():
'''Execute action'''
quota_obj = NetAppONTAPQuotas()
quota_obj.apply()


if __name__ == '__main__':
main()