File: //usr/lib/python3.9/site-packages/ansible_collections/netapp/ontap/plugins/modules/na_ontap_nfs.py
# (c) 2018-2022, 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
DOCUMENTATION = """
module: na_ontap_nfs
short_description: NetApp ONTAP NFS status
extends_documentation_fragment:
- netapp.ontap.netapp.na_ontap
version_added: 2.6.0
author: NetApp Ansible Team (@carchi8py) <[email protected]>
description:
- Enable or disable NFS on ONTAP
options:
state:
description:
- Whether NFS should exist or not.
choices: ['present', 'absent']
type: str
default: present
service_state:
description:
- Whether the specified NFS should be enabled or disabled. Creates NFS service if doesnt exist.
choices: ['started', 'stopped']
type: str
vserver:
description:
- Name of the vserver to use.
required: true
type: str
nfsv3:
description:
- status of NFSv3.
choices: ['enabled', 'disabled']
type: str
nfsv3_fsid_change:
description:
- status of if NFSv3 clients see change in FSID as they traverse filesystems.
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv4_fsid_change:
description:
- status of if NFSv4 clients see change in FSID as they traverse filesystems.
choices: ['enabled', 'disabled']
type: str
version_added: 2.9.0
nfsv4:
description:
- status of NFSv4.
choices: ['enabled', 'disabled']
type: str
nfsv41:
description:
- status of NFSv41.
- usage of C(nfsv4.1) is deprecated as it does not match Ansible naming convention. The alias will be removed.
- please use C(nfsv41) exclusively for this option.
aliases: ['nfsv4.1']
choices: ['enabled', 'disabled']
type: str
nfsv41_pnfs:
description:
- status of NFSv41 pNFS.
choices: ['enabled', 'disabled']
type: str
version_added: 2.9.0
nfsv4_numeric_ids:
description:
- status of NFSv4 numeric ID's.
choices: ['enabled', 'disabled']
type: str
version_added: 2.9.0
vstorage_state:
description:
- status of vstorage_state.
choices: ['enabled', 'disabled']
type: str
nfsv4_id_domain:
description:
- Name of the nfsv4_id_domain to use.
type: str
nfsv40_acl:
description:
- status of NFS v4.0 ACL feature
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv40_read_delegation:
description:
- status for NFS v4.0 read delegation feature.
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv40_write_delegation:
description:
- status for NFS v4.0 write delegation feature.
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv41_acl:
description:
- status of NFS v4.1 ACL feature
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv41_read_delegation:
description:
- status for NFS v4.1 read delegation feature.
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv41_write_delegation:
description:
- status for NFS v4.1 write delegation feature.
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
nfsv40_referrals:
description:
- status for NFS v4.0 referrals.
choices: ['enabled', 'disabled']
type: str
version_added: 2.9.0
nfsv41_referrals:
description:
- status for NFS v4.1 referrals.
choices: ['enabled', 'disabled']
type: str
version_added: 2.9.0
tcp:
description:
- Enable TCP (support from ONTAP 9.3 onward).
choices: ['enabled', 'disabled']
type: str
udp:
description:
- Enable UDP (support from ONTAP 9.3 onward).
choices: ['enabled', 'disabled']
type: str
showmount:
description:
- Whether SVM allows showmount.
- With REST, supported from ONTAP 9.8 version.
choices: ['enabled', 'disabled']
type: str
version_added: 2.7.0
tcp_max_xfer_size:
description:
- TCP Maximum Transfer Size (bytes). The default value is 65536.
- This option requires ONTAP 9.11.0 or later in REST.
version_added: 2.8.0
type: int
windows:
description:
- This option can be set or modified when using REST.
- It requires ONTAP 9.11.0 or later.
version_added: 22.3.0
type: dict
suboptions:
default_user:
description:
- Specifies the default Windows user for the NFS server.
type: str
map_unknown_uid_to_default_user:
description:
- Specifies whether or not the mapping of an unknown UID to the default Windows user is enabled.
type: bool
v3_ms_dos_client_enabled:
description:
- Specifies whether NFSv3 MS-DOS client support is enabled.
type: bool
root:
description:
- This option can be set or modified when using REST.
- It requires ONTAP 9.11.0 or later.
type: dict
version_added: 22.3.0
suboptions:
ignore_nt_acl:
description:
- Specifies whether Windows ACLs affect root access from NFS.
- If this option is enabled, root access from NFS ignores the NT ACL set on the file or directory.
type: bool
skip_write_permission_check:
description:
- Specifies if permission checks are to be skipped for NFS WRITE calls from root/owner.
- For copying read-only files to a destination folder which has inheritable ACLs, this option must be enabled.
type: bool
security:
description:
- This option can be set or modified when using REST.
- It requires ONTAP 9.11.0 or later.
type: dict
version_added: 22.3.0
suboptions:
chown_mode:
description:
- Specifies whether file ownership can be changed only by the superuser, or if a non-root user can also change file ownership.
- If this option is set to restricted, file ownership can be changed only by the superuser,
even though the on-disk permissions allow a non-root user to change file ownership.
- If this option is set to unrestricted, file ownership can be changed by the superuser and by the non-root user,
depending upon the access granted by on-disk permissions.
- If this option is set to use-export-policy, file ownership can be changed in accordance with the relevant export rules.
choices: ['restricted', 'unrestricted', 'use_export_policy']
type: str
nt_acl_display_permission:
description:
- Controls the permissions that are displayed to NFSv3 and NFSv4 clients on a file or directory that has an NT ACL set.
- When true, the displayed permissions are based on the maximum access granted by the NT ACL to any user.
- When false, the displayed permissions are based on the minimum access granted by the NT ACL to any user.
type: bool
ntfs_unix_security:
description:
- Specifies how NFSv3 security changes affect NTFS volumes.
- If this option is set to ignore, ONTAP ignores NFSv3 security changes.
- If this option is set to fail, this overrides the UNIX security options set in the relevant export rules.
- If this option is set to use_export_policy, ONTAP processes NFSv3 security changes in accordance with the relevant export rules.
choices: ['ignore', 'fail', 'use_export_policy']
type: str
permitted_encryption_types:
description:
- Specifies the permitted encryption types for Kerberos over NFS.
type: list
elements: str
rpcsec_context_idle:
description:
- Specifies, in seconds, the amount of time a RPCSEC_GSS context is permitted to remain unused before it is deleted.
type: int
"""
EXAMPLES = """
- name: change nfs status
netapp.ontap.na_ontap_nfs:
state: present
service_state: stopped
vserver: vs_hack
nfsv3: disabled
nfsv4: disabled
nfsv41: enabled
tcp: disabled
udp: disabled
vstorage_state: disabled
nfsv4_id_domain: example.com
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: create nfs configuration - REST
netapp.ontap.na_ontap_nfs:
state: present
service_state: stopped
vserver: vs_hack
nfsv3: disabled
nfsv4: disabled
nfsv41: enabled
tcp: disabled
udp: disabled
vstorage_state: disabled
nfsv4_id_domain: example.com
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Modify nfs configuration - REST
netapp.ontap.na_ontap_nfs:
state: present
vserver: vs_hack
root:
ignore_nt_acl: true
skip_write_permission_check: true
security:
chown_mode: restricted
nt_acl_display_permission: true
ntfs_unix_security: fail
rpcsec_context_idle: 5
windows:
v3_ms_dos_client_enabled: true
map_unknown_uid_to_default_user: false
default_user: test_user
tcp_max_xfer_size: 16384
hostname: "{{ netapp_hostname }}"
username: "{{ netapp_username }}"
password: "{{ netapp_password }}"
- name: Delete nfs configuration
netapp.ontap.na_ontap_nfs:
state: absent
vserver: vs_hack
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_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
from ansible_collections.netapp.ontap.plugins.module_utils.netapp_module import NetAppModule
from ansible_collections.netapp.ontap.plugins.module_utils import rest_generic
class NetAppONTAPNFS:
""" object initialize and class methods """
def __init__(self):
self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
service_state=dict(required=False, type='str', choices=['started', 'stopped']),
vserver=dict(required=True, type='str'),
nfsv3=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv3_fsid_change=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv4_fsid_change=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv4=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv41=dict(required=False, type='str', default=None, choices=['enabled', 'disabled'], aliases=['nfsv4.1']),
nfsv41_pnfs=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv4_numeric_ids=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
vstorage_state=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
tcp=dict(required=False, default=None, type='str', choices=['enabled', 'disabled']),
udp=dict(required=False, default=None, type='str', choices=['enabled', 'disabled']),
nfsv4_id_domain=dict(required=False, type='str', default=None),
nfsv40_acl=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv40_read_delegation=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv40_referrals=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv40_write_delegation=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv41_acl=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv41_read_delegation=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv41_referrals=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
nfsv41_write_delegation=dict(required=False, type='str', default=None, choices=['enabled', 'disabled']),
showmount=dict(required=False, default=None, type='str', choices=['enabled', 'disabled']),
tcp_max_xfer_size=dict(required=False, default=None, type='int'),
# security
security=dict(type='dict', options=dict(
rpcsec_context_idle=dict(required=False, type='int'),
ntfs_unix_security=dict(required=False, type='str', choices=['ignore', 'fail', 'use_export_policy']),
chown_mode=dict(required=False, type='str', choices=['restricted', 'unrestricted', 'use_export_policy']),
nt_acl_display_permission=dict(required=False, type='bool'),
permitted_encryption_types=dict(type='list', elements='str', required=False),
)),
# root
root=dict(type='dict', options=dict(
ignore_nt_acl=dict(required=False, type='bool'),
skip_write_permission_check=dict(required=False, type='bool'),
)),
# windows
windows=dict(type='dict', options=dict(
map_unknown_uid_to_default_user=dict(required=False, type='bool'),
v3_ms_dos_client_enabled=dict(required=False, type='bool'),
default_user=dict(required=False, type='str'),
)),
))
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)
self.zapi_names = {
'nfsv3': 'is-nfsv3-enabled', # REST: protocol.v3_enabled
'nfsv3_fsid_change': 'is-nfsv3-fsid-change-enabled',
'nfsv4_fsid_change': 'is-nfsv4-fsid-change-enabled',
'nfsv4': 'is-nfsv40-enabled', # REST: protocol.v40_enabled
'nfsv41': 'is-nfsv41-enabled', # REST: protocol.v41_enabled
'nfsv41_pnfs': 'is-nfsv41-pnfs-enabled', # protocol.v41_features.pnfs_enabled
'nfsv4_numeric_ids': 'is-nfsv4-numeric-ids-enabled',
'vstorage_state': 'is-vstorage-enabled', # REST: vstorage_enabled
'nfsv4_id_domain': 'nfsv4-id-domain', # REST: protocol.v4_id_domain
'tcp': 'is-tcp-enabled', # REST: transport.tcp_enabled
'udp': 'is-udp-enabled', # REST: transport.udp_enabled
'nfsv40_acl': 'is-nfsv40-acl-enabled', # REST: protocol.v40_features.acl_enabled
'nfsv40_read_delegation': 'is-nfsv40-read-delegation-enabled', # REST: protocol.v40_features.read_delegation_enabled
'nfsv40_referrals': 'is-nfsv40-referrals-enabled',
'nfsv40_write_delegation': 'is-nfsv40-write-delegation-enabled', # REST: protocol.v40_features.write_delegation_enabled
'nfsv41_acl': 'is-nfsv41-acl-enabled', # REST: protocol.v41_features.acl_enabled
'nfsv41_read_delegation': 'is-nfsv41-read-delegation-enabled', # REST: protocol.v41_features.read_delegation_enabled
'nfsv41_referrals': 'is-nfsv41-referrals-enabled',
'nfsv41_write_delegation': 'is-nfsv41-write-delegation-enabled', # REST: protocol.v41_features.write_delegation_enabled
'showmount': 'showmount', # REST: showmount_enabled
'tcp_max_xfer_size': 'tcp-max-xfer-size'
}
self.rest_api = netapp_utils.OntapRestAPI(self.module)
unsupported_rest_properties = ['nfsv3_fsid_change',
'nfsv4_fsid_change',
'nfsv4_numeric_ids',
'nfsv40_referrals',
'nfsv41_referrals']
partially_supported_rest_properties = [['showmount', (9, 8)], ['root', (9, 11, 0)], ['windows', (9, 11, 0)], ['security', (9, 11, 0)],
['tcp_max_xfer_size', (9, 11, 0)]]
self.use_rest = self.rest_api.is_rest_supported_properties(self.parameters, unsupported_rest_properties, partially_supported_rest_properties)
if 'nfsv4.1' in self.parameters:
self.module.warn('Error: "nfsv4.1" option conflicts with Ansible naming conventions - please use "nfsv41".')
self.svm_uuid = None
self.unsupported_zapi_properties = ['root', 'windows', 'security']
self.parameters = self.na_helper.filter_out_none_entries(self.parameters)
if not self.use_rest:
if not netapp_utils.has_netapp_lib():
self.module.fail_json(msg=netapp_utils.netapp_lib_is_required())
for unsupported_zapi_property in self.unsupported_zapi_properties:
if self.parameters.get(unsupported_zapi_property) is not None:
msg = "Error: %s option is not supported with ZAPI. It can only be used with REST." % unsupported_zapi_property
self.module.fail_json(msg=msg)
self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
def get_nfs_service(self):
if self.use_rest:
return self.get_nfs_service_rest()
nfs_get_iter = netapp_utils.zapi.NaElement('nfs-service-get-iter')
nfs_info = netapp_utils.zapi.NaElement('nfs-info')
nfs_info.add_new_child('vserver', self.parameters['vserver'])
query = netapp_utils.zapi.NaElement('query')
query.add_child_elem(nfs_info)
nfs_get_iter.add_child_elem(query)
result = self.server.invoke_successfully(nfs_get_iter, True)
if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
return self.format_return(result)
return None
def format_return(self, result):
attributes_list = result.get_child_by_name('attributes-list').get_child_by_name('nfs-info')
return {
'nfsv3': self.convert_from_bool(attributes_list.get_child_content('is-nfsv3-enabled')),
'nfsv3_fsid_change': self.convert_from_bool(attributes_list.get_child_content('is-nfsv3-fsid-change-enabled')),
'nfsv4_fsid_change': self.convert_from_bool(attributes_list.get_child_content('is-nfsv4-fsid-change-enabled')),
'nfsv4': self.convert_from_bool(attributes_list.get_child_content('is-nfsv40-enabled')),
'nfsv41': self.convert_from_bool(attributes_list.get_child_content('is-nfsv41-enabled')),
'nfsv41_pnfs': self.convert_from_bool(attributes_list.get_child_content('is-nfsv41-pnfs-enabled')),
'nfsv4_numeric_ids': self.convert_from_bool(attributes_list.get_child_content('is-nfsv4-numeric-ids-enabled')),
'vstorage_state': self.convert_from_bool(attributes_list.get_child_content('is-vstorage-enabled')),
'nfsv4_id_domain': attributes_list.get_child_content('nfsv4-id-domain'),
'tcp': self.convert_from_bool(attributes_list.get_child_content('is-tcp-enabled')),
'udp': self.convert_from_bool(attributes_list.get_child_content('is-udp-enabled')),
'nfsv40_acl': self.convert_from_bool(attributes_list.get_child_content('is-nfsv40-acl-enabled')),
'nfsv40_read_delegation': self.convert_from_bool(attributes_list.get_child_content('is-nfsv40-read-delegation-enabled')),
'nfsv40_referrals': self.convert_from_bool(attributes_list.get_child_content('is-nfsv40-referrals-enabled')),
'nfsv40_write_delegation': self.convert_from_bool(attributes_list.get_child_content('is-nfsv40-write-delegation-enabled')),
'nfsv41_acl': self.convert_from_bool(attributes_list.get_child_content('is-nfsv41-acl-enabled')),
'nfsv41_read_delegation': self.convert_from_bool(attributes_list.get_child_content('is-nfsv41-read-delegation-enabled')),
'nfsv41_referrals': self.convert_from_bool(attributes_list.get_child_content('is-nfsv41-referrals-enabled')),
'nfsv41_write_delegation': self.convert_from_bool(attributes_list.get_child_content('is-nfsv41-write-delegation-enabled')),
'showmount': self.convert_from_bool(attributes_list.get_child_content('showmount')),
'tcp_max_xfer_size': self.na_helper.get_value_for_int(True, attributes_list.get_child_content('tcp-max-xfer-size'))
}
def get_nfs_status(self):
nfs_status = netapp_utils.zapi.NaElement('nfs-status')
result = self.server.invoke_successfully(nfs_status, True)
return result.get_child_content('is-enabled')
def create_nfs_service(self):
if self.use_rest:
return self.create_nfs_service_rest()
# This is what the old module did, not sure what happens if nfs dosn't exist.
self.enable_nfs()
def enable_nfs(self):
"""
enable nfs (online). If the NFS service was not explicitly created,
this API will create one with default options.
"""
nfs_enable = netapp_utils.zapi.NaElement.create_node_with_children('nfs-enable')
try:
self.server.invoke_successfully(nfs_enable, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error changing the service_state of nfs %s to %s: %s' %
(self.parameters['vserver'], self.parameters['service_state'], to_native(error)),
exception=traceback.format_exc())
def disable_nfs(self):
"""
disable nfs (offline).
"""
nfs_disable = netapp_utils.zapi.NaElement.create_node_with_children('nfs-disable')
try:
self.server.invoke_successfully(nfs_disable, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error changing the service_state of nfs %s to %s: %s' %
(self.parameters['vserver'], self.parameters['service_state'], to_native(error)),
exception=traceback.format_exc())
def modify_nfs_service(self, modify):
if self.use_rest:
return self.modify_nfs_service_rest(modify)
# This is what the old module did, not sure what happens if nfs dosn't exist.
nfs_modify = netapp_utils.zapi.NaElement('nfs-service-modify')
service_state = modify.pop('service_state', None)
self.modify_service_state(service_state)
for each in modify:
if each in ['nfsv4_id_domain', 'tcp_max_xfer_size']:
nfs_modify.add_new_child(self.zapi_names[each], str(modify[each]))
else:
nfs_modify.add_new_child(self.zapi_names[each], self.convert_to_bool(modify[each]))
try:
self.server.invoke_successfully(nfs_modify, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error modifying nfs: %s' % (to_native(error)),
exception=traceback.format_exc())
def modify_service_state(self, service_state):
nfs_enabled = self.get_nfs_status()
if service_state == 'started' and nfs_enabled == 'false':
self.enable_nfs()
elif service_state == 'stopped' and nfs_enabled == 'true':
self.disable_nfs()
def delete_nfs_service(self):
"""
delete nfs service.
"""
if self.use_rest:
return self.delete_nfs_service_rest()
nfs_delete = netapp_utils.zapi.NaElement.create_node_with_children('nfs-service-destroy')
try:
self.server.invoke_successfully(nfs_delete, enable_tunneling=True)
except netapp_utils.zapi.NaApiError as error:
self.module.fail_json(msg='Error deleting nfs: %s' % (to_native(error)),
exception=traceback.format_exc())
def get_nfs_service_rest(self):
api = 'protocols/nfs/services'
params = {'svm.name': self.parameters['vserver'],
'fields': 'protocol.v3_enabled,'
'protocol.v40_enabled,'
'protocol.v41_enabled,'
'protocol.v41_features.pnfs_enabled,'
'vstorage_enabled,'
'protocol.v4_id_domain,'
'transport.tcp_enabled,'
'transport.udp_enabled,'
'protocol.v40_features.acl_enabled,'
'protocol.v40_features.read_delegation_enabled,'
'protocol.v40_features.write_delegation_enabled,'
'protocol.v41_features.acl_enabled,'
'protocol.v41_features.read_delegation_enabled,'
'protocol.v41_features.write_delegation_enabled,'
'enabled,'
'svm.uuid,'}
if self.parameters.get('showmount'):
params['fields'] += 'showmount_enabled,'
if self.rest_api.meets_rest_minimum_version(self.use_rest, 9, 11, 0):
params['fields'] += 'root.*,security.*,windows.*,transport.tcp_max_transfer_size'
# TODO: might return more than 1 record, find out
record, error = rest_generic.get_one_record(self.rest_api, api, params)
if error:
self.module.fail_json(msg='Error getting nfs services for SVM %s: %s' % (self.parameters['vserver'], to_native(error)),
exception=traceback.format_exc())
return self.format_get_nfs_service_rest(record) if record else record
def format_get_nfs_service_rest(self, record):
return {
'nfsv3': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v3_enabled'])),
'nfsv4': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v40_enabled'])),
'nfsv41': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v41_enabled'])),
'nfsv41_pnfs': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v41_features', 'pnfs_enabled'])),
'vstorage_state': self.convert_from_bool(self.na_helper.safe_get(record, ['vstorage_enabled'])),
'nfsv4_id_domain': self.na_helper.safe_get(record, ['protocol', 'v4_id_domain']),
'tcp': self.convert_from_bool(self.na_helper.safe_get(record, ['transport', 'tcp_enabled'])),
'udp': self.convert_from_bool(self.na_helper.safe_get(record, ['transport', 'udp_enabled'])),
'tcp_max_xfer_size': self.na_helper.safe_get(record, ['transport', 'tcp_max_transfer_size']),
'nfsv40_acl': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v40_features', 'acl_enabled'])),
'nfsv40_read_delegation': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v40_features', 'read_delegation_enabled'])),
'nfsv40_write_delegation': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v40_features', 'write_delegation_enabled'])),
'nfsv41_acl': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v41_features', 'acl_enabled'])),
'nfsv41_read_delegation': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v41_features', 'read_delegation_enabled'])),
'nfsv41_write_delegation': self.convert_from_bool(self.na_helper.safe_get(record, ['protocol', 'v41_features', 'write_delegation_enabled'])),
'showmount': self.convert_from_bool(self.na_helper.safe_get(record, ['showmount_enabled'])),
'svm_uuid': self.na_helper.safe_get(record, ['svm', 'uuid']),
'service_state': self.convert_from_bool_to_started(self.na_helper.safe_get(record, ['enabled'])),
'root': self.na_helper.safe_get(record, ['root']),
'windows': self.na_helper.safe_get(record, ['windows']),
'security': self.na_helper.safe_get(record, ['security']),
}
def create_nfs_service_rest(self):
api = 'protocols/nfs/services'
body = {'svm.name': self.parameters['vserver']}
body.update(self.create_modify_body(body))
dummy, error = rest_generic.post_async(self.rest_api, api, body, job_timeout=120)
if error:
self.module.fail_json(msg='Error creating nfs service for SVM %s: %s' % (self.parameters['vserver'], to_native(error)),
exception=traceback.format_exc())
def delete_nfs_service_rest(self):
if self.svm_uuid is None:
self.module.fail_json(msg='Error deleting nfs service for SVM %s: svm.uuid is None' % self.parameters['vserver'])
dummy, error = rest_generic.delete_async(self.rest_api, 'protocols/nfs/services', self.svm_uuid, job_timeout=120)
if error:
self.module.fail_json(msg='Error deleting nfs service for SVM %s' % self.parameters['vserver'])
def modify_nfs_service_rest(self, modify):
if self.svm_uuid is None:
self.module.fail_json(msg='Error modifying nfs service for SVM %s: svm.uuid is None' % self.parameters['vserver'])
api = 'protocols/nfs/services'
body = {}
body.update(self.create_modify_body(body, modify))
dummy, error = rest_generic.patch_async(self.rest_api, api, self.svm_uuid, body, job_timeout=120)
if error:
self.module.fail_json(msg='Error modifying nfs service for SVM %s: %s' % (self.parameters['vserver'], to_native(error)),
exception=traceback.format_exc())
def create_modify_body(self, body, modify=None):
params = modify or self.parameters
if params.get('nfsv3') is not None:
body['protocol.v3_enabled'] = self.convert_to_bool(params['nfsv3'])
if params.get('nfsv4') is not None:
body['protocol.v40_enabled'] = self.convert_to_bool(params['nfsv4'])
if params.get('nfsv41') is not None:
body['protocol.v41_enabled'] = self.convert_to_bool(params['nfsv41'])
if params.get('nfsv41_pnfs') is not None:
body['protocol.v41_features.pnfs_enabled'] = self.convert_to_bool(params['nfsv41_pnfs'])
if params.get('vstorage_state') is not None:
body['vstorage_enabled'] = self.convert_to_bool(params['vstorage_state'])
if params.get('nfsv4_id_domain') is not None:
body['protocol.v4_id_domain'] = params['nfsv4_id_domain']
if params.get('tcp') is not None:
body['transport.tcp_enabled'] = self.convert_to_bool(params['tcp'])
if params.get('udp') is not None:
body['transport.udp_enabled'] = self.convert_to_bool(params['udp'])
if params.get('nfsv40_acl') is not None:
body['protocol.v40_features.acl_enabled'] = self.convert_to_bool(params['nfsv40_acl'])
if params.get('nfsv40_read_delegation') is not None:
body['protocol.v40_features.read_delegation_enabled'] = self.convert_to_bool(params['nfsv40_read_delegation'])
if params.get('nfsv40_write_delegation') is not None:
body['protocol.v40_features.write_delegation_enabled'] = self.convert_to_bool(params['nfsv40_write_delegation'])
if params.get('nfsv41_acl') is not None:
body['protocol.v41_features.acl_enabled'] = self.convert_to_bool(params['nfsv41_acl'])
if params.get('nfsv41_read_delegation') is not None:
body['protocol.v41_features.read_delegation_enabled'] = self.convert_to_bool(params['nfsv41_read_delegation'])
if params.get('nfsv41_write_delegation') is not None:
body['protocol.v41_features.write_delegation_enabled'] = self.convert_to_bool(params['nfsv41_write_delegation'])
if params.get('showmount') is not None:
body['showmount_enabled'] = self.convert_to_bool(params['showmount'])
# Tested this out, in both create and modify, changing the service_state will enable and disabled the service
# during both a create and modify.
if params.get('service_state') is not None:
body['enabled'] = self.convert_to_bool(params['service_state'])
if params.get('root') is not None:
body['root'] = params['root']
if params.get('windows') is not None:
body['windows'] = params['windows']
if params.get('security') is not None:
body['security'] = params['security']
if params.get('tcp_max_xfer_size') is not None:
body['transport.tcp_max_transfer_size'] = params['tcp_max_xfer_size']
return body
def convert_to_bool(self, value):
return 'true' if value in ['enabled', 'started'] else 'false'
def convert_from_bool(self, value):
return 'enabled' if value in ['true', True] else 'disabled'
def convert_from_bool_to_started(self, value):
return 'started' if value in ['true', True] else 'stopped'
def validate_modify(self, current, modify):
'''Earlier ONTAP versions do not support tcp_max_xfer_size'''
if 'tcp_max_xfer_size' in modify and current['tcp_max_xfer_size'] is None:
self.module.fail_json(msg='Error: tcp_max_xfer_size is not supported on ONTAP 9.3 or earlier.')
def apply(self):
current = self.get_nfs_service()
if self.use_rest and current is not None:
self.svm_uuid = current.get('svm_uuid')
cd_action = self.na_helper.get_cd_action(current, self.parameters)
modify = None
if cd_action is None and self.parameters['state'] == 'present':
modify = self.na_helper.get_modified_attributes(current, self.parameters)
if not self.use_rest:
self.validate_modify(current, modify)
if self.na_helper.changed and not self.module.check_mode:
if cd_action == 'create':
self.create_nfs_service()
elif cd_action == 'delete':
self.delete_nfs_service()
elif modify:
self.modify_nfs_service(modify)
result = netapp_utils.generate_result(self.na_helper.changed, cd_action, modify)
self.module.exit_json(**result)
def main():
""" Create object and call apply """
obj = NetAppONTAPNFS()
obj.apply()
if __name__ == '__main__':
main()