diff --git a/ansible_collections/netapp/ontap/README.md b/ansible_collections/netapp/ontap/README.md index 53a6f3e9..b7bccb21 100644 --- a/ansible_collections/netapp/ontap/README.md +++ b/ansible_collections/netapp/ontap/README.md @@ -42,6 +42,7 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack) ### Minor changes - na_ontap_firmware_upgrade - Added a new 'storage' type as default firmware_type. - na_ontap_info - deprecate ``state`` option. + - na_ontap_quota - allow to turn quota on/off without providing quota_target or type. - na_ontap_rest_info - deprecate ``state`` option. - na_ontap_snapmirror - use REST API for create action if target supports it. (ZAPIs are still used for all other actions). - na_ontap_volume - use REST API for delete operation if targets supports it. @@ -54,6 +55,7 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack) - na_ontap_snapmirror - report error when attempting to change relationship_type. - na_ontap_snapmirror - fix job update failures for load_sharing mirrors. - na_ontap_snapmirror - wait up to 5 minutes for abort to complete before issuing a delete. + - na_ontap_snmp - SNMP module wrong access_control issue and error handling fix. - na_ontap_volume - REST expects 'all' for tiering policy and not 'backup'. ## 20.12.0 diff --git a/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-2491.yaml b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-2491.yaml new file mode 100644 index 00000000..aaa49833 --- /dev/null +++ b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-2491.yaml @@ -0,0 +1,2 @@ +bugfixes: + - na_ontap_snmp - SNMP module wrong ``access_control`` issue and error handling fix. diff --git a/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3510.yaml b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3510.yaml new file mode 100644 index 00000000..321171b8 --- /dev/null +++ b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3510.yaml @@ -0,0 +1,2 @@ +minor_changes: + - na_ontap_quota - allow to turn quota on/off without providing quota_target or type. diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_quotas.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_quotas.py index 02bfe6bf..5cdc7a32 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_quotas.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_quotas.py @@ -6,6 +6,9 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +''' +na_ontap_quotas +''' ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], @@ -41,7 +44,7 @@ quota_target: description: - The quota target of the type specified. - required: true + - Required to create or modify a rule. type: str qtree: description: @@ -53,8 +56,8 @@ type: description: - The type of quota rule + - Required to create or modify a rule. choices: ['user', 'group', 'tree'] - required: true type: str policy: description: @@ -196,9 +199,9 @@ def __init__(self): 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'), + quota_target=dict(required=False, type='str'), qtree=dict(required=False, type='str', default=""), - type=dict(required=True, type='str', choices=['user', 'group', 'tree']), + type=dict(required=False, type='str', choices=['user', 'group', 'tree']), policy=dict(required=False, type='str'), set_quota_status=dict(required=False, type='bool'), perform_user_mapping=dict(required=False, type='bool'), @@ -212,6 +215,16 @@ def __init__(self): self.module = AnsibleModule( argument_spec=self.argument_spec, + required_by={ + 'policy': ['quota_target', 'type'], + 'perform_user_mapping': ['quota_target', 'type'], + 'file_limit': ['quota_target', 'type'], + 'disk_limit': ['quota_target', 'type'], + 'soft_file_limit': ['quota_target', 'type'], + 'soft_disk_limit': ['quota_target', 'type'], + 'threshold': ['quota_target', 'type'], + }, + required_together=[['quota_target', 'type']], supports_check_mode=True ) @@ -219,7 +232,7 @@ def __init__(self): self.parameters = self.na_helper.set_parameters(self.module.params) # converted blank parameter to * as shown in vsim - if self.parameters['quota_target'] == "": + if self.parameters.get('quota_target') == "": self.parameters['quota_target'] = '*' if HAS_NETAPP_LIB is False: @@ -254,6 +267,8 @@ def get_quotas(self): Get quota details :return: name of volume if quota exists, None otherwise """ + if self.parameters.get('type') is None: + return None quota_get = netapp_utils.zapi.NaElement('quota-list-entries-iter') query = { 'query': { @@ -407,13 +422,15 @@ def apply(self): Apply action to quotas """ netapp_utils.ems_log_event("na_ontap_quotas", self.server) + cd_action = None modify_quota_status = None modify_quota = None quota_status = None current = self.get_quotas() - 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.parameters.get('type') is not None: + 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 'set_quota_status' in self.parameters or modify_quota: quota_status = self.get_quota_status() if 'set_quota_status' in self.parameters and quota_status is not None: diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snapmirror.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snapmirror.py index 60178306..870f104c 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snapmirror.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snapmirror.py @@ -186,19 +186,16 @@ - Cloud tiering policy. type: dict suboptions: - control: - description: storage tiering placement rules for the container. - choices: ['required', 'best_effort', 'disallowed'] - type: str policy: description: - Cloud tiering policy. choices: ['all', 'auto', 'none', 'snapshot-only'] type: str - object_stores: - description: list of object store names for tiering. - type: list - elements: str + supported: + description: + - enable provisioning of the destination endpoint volumes on FabricPool aggregates. + - only supported for FlexVol volume, FlexGroup volume, and Consistency Group endpoints. + type: bool destination_cluster: description: - Requires ONTAP 9.7 or higher. @@ -426,9 +423,8 @@ def __init__(self): name=dict(type='str', choices=['value', 'performance', 'extreme']), )), tiering=dict(type='dict', options=dict( - control=dict(type='str', choices=['required', 'best_effort', 'disallowed']), policy=dict(type='str', choices=['all', 'auto', 'none', 'snapshot-only']), - object_stores=dict(type='list', elements='str') # create only + supported=dict(type='bool') )), )), source_cluster=dict(required=False, type='str'), diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snmp.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snmp.py index ae49a721..f8477cd3 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snmp.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_snmp.py @@ -23,9 +23,10 @@ module: na_ontap_snmp options: access_control: + choices: ['ro'] description: - "Access control for the community. The only supported value is 'ro' (read-only)" - required: true + default: 'ro' type: str community_name: description: @@ -63,8 +64,11 @@ RETURN = """ """ +import traceback from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +from ansible_collections.netapp.ontap.plugins.module_utils.netapp_module import NetAppModule HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() @@ -78,19 +82,18 @@ def __init__(self): self.argument_spec.update(dict( state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), community_name=dict(required=True, type='str'), - access_control=dict(required=True, type='str'), + access_control=dict(required=False, type='str', choices=['ro'], default='ro'), )) self.module = AnsibleModule( argument_spec=self.argument_spec, - supports_check_mode=False + supports_check_mode=True ) - parameters = self.module.params + self.na_helper = NetAppModule() + # set up state variables - self.state = parameters['state'] - self.community_name = parameters['community_name'] - self.access_control = parameters['access_control'] + 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") @@ -103,13 +106,37 @@ def invoke_snmp_community(self, zapi): @return: SUCCESS / FAILURE with an error_message """ snmp_community = netapp_utils.zapi.NaElement.create_node_with_children( - zapi, **{'community': self.community_name, - 'access-control': self.access_control}) + zapi, **{'community': self.parameters['community_name'], + 'access-control': self.parameters['access_control']}) try: self.server.invoke_successfully(snmp_community, enable_tunneling=True) - except netapp_utils.zapi.NaApiError: # return False for duplicate entry - return False - return True + except netapp_utils.zapi.NaApiError as error: + if zapi == 'snmp-community-add': + action = 'adding' + elif zapi == 'snmp-community-delete': + action = 'deleting' + else: + action = 'unexpected' + self.module.fail_json(msg='Error %s community %s: %s' % (action, self.parameters['community_name'], to_native(error)), + exception=traceback.format_exc()) + + def get_snmp(self): + """ + Check if SNMP community exists + """ + snmp_obj = netapp_utils.zapi.NaElement('snmp-status') + try: + result = self.server.invoke_successfully(snmp_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg=to_native(error), exception=traceback.format_exc()) + if result.get_child_by_name('communities') is not None: + for snmp_entry in result.get_child_by_name('communities').get_children(): + community_name = snmp_entry.get_child_content('community') + if community_name == self.parameters['community_name']: + return_values = {'community_name': snmp_entry.get_child_content('community'), + 'access_control': snmp_entry.get_child_content('access-control')} + return return_values + return None def add_snmp_community(self): """ @@ -131,17 +158,18 @@ def apply(self): to add an already existing snmp community """ changed = False + current = self.get_snmp() results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event("na_ontap_snmp", cserver) - if self.state == 'present': # add - if self.add_snmp_community(): - changed = True - elif self.state == 'absent': # delete - if self.delete_snmp_community(): - changed = True - - self.module.exit_json(changed=changed) + cd_action = self.na_helper.get_cd_action(current, self.parameters) + if self.na_helper.changed and not self.module.check_mode: + if cd_action == 'create': + self.add_snmp_community() + elif cd_action == 'delete': + self.delete_snmp_community() + + self.module.exit_json(changed=self.na_helper.changed) def main():