diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f59815dc..f783d12b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,8 +12,7 @@ # Jennifer John (Jennifer-John) # Meenakshi Dembi (meenakshidembi691) # Pavan Mudunuri (Pavan-Mudunuri) -# Previnkumar G (Previnkumar-G) # Trisha Datta (trisha-dell) # for all files: -* @kuttattz @Bhavneet-Sharma @Jennifer-John @meenakshidembi691 @Pavan-Mudunuri @Previnkumar-G @trisha-dell +* @kuttattz @Bhavneet-Sharma @Jennifer-John @meenakshidembi691 @Pavan-Mudunuri @trisha-dell diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eaf66cbf..fc7a95d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,15 +4,32 @@ Dellemc.Powerscale Change Logs .. contents:: Topics + +v2.2.0 +====== + +Minor Changes +------------- + +- Added support for listing NFS default settings, NFS global settings and NFS zone settings in Info module. +- Added support for specifying the users and groups to which non-root and root clients are mapped in nfs module. + +New Modules +----------- + +- dellemc.powerscale.nfs_default_settings - Get details and modify NFS default settings. +- dellemc.powerscale.nfs_global_settings - Get details and modify NFS global settings. +- dellemc.powerscale.nfs_zone_settings - Get details and modify NFS zone settings. + v2.1.0 ====== Minor Changes ------------- +- Added support for SmartConnect zone alaises(DNS names) in network pool module. - Added support for deleting an access zone and reordering the authentication providers in access zone module. - Added support for service principal names(SPN) in AD module. -- Added support for SmartConnect zone alaises(DNS names) in network pool module. New Modules ----------- @@ -25,10 +42,10 @@ v2.0.0 Minor Changes ------------- -- Added support for ignoring unresolvable hosts for NFS Export. +- Added support for PowerScale OneFS 9.5 Islander release. - Added support for everyone user in filesystem module. +- Added support for ignoring unresolvable hosts for NFS Export. - Added support for listing LDAP auth providers and user mapping rules in Info module. -- Added support for PowerScale OneFS 9.5 Islander release. - For the execution of the PowerScale Ansible modules, python library "isilon-sdk" needs to be installed. New Modules diff --git a/README.md b/README.md index 4dc825a2..79fa3b53 100644 --- a/README.md +++ b/README.md @@ -28,20 +28,23 @@ The Ansible Modules for Dell PowerScale support the following features: - Find and close the SMB open files. - Create, modify, get details and delete a user mapping rule. - Create, modify, get details and delete an S3 bucket. +- Get details and modify NFS default settings. +- Get details and modify NFS global settings. +- Get details and modify NFS zone settings. The tasks can be executed by running simple playbooks written in yaml syntax. ## Table of contents -* [Code of conduct](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/CODE_OF_CONDUCT.md) -* [Maintainer guide](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/MAINTAINER_GUIDE.md) -* [Committer guide](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/COMMITTER_GUIDE.md) -* [Contributing guide](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/CONTRIBUTING.md) -* [Branching strategy](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/BRANCHING.md) -* [List of adopters](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/ADOPTERS.md) -* [Maintainers](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/MAINTAINERS.md) -* [Support](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/SUPPORT.md) -* [Security](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/SECURITY.md) +* [Code of conduct](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/CODE_OF_CONDUCT.md) +* [Maintainer guide](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/MAINTAINER_GUIDE.md) +* [Committer guide](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/COMMITTER_GUIDE.md) +* [Contributing guide](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/CONTRIBUTING.md) +* [Branching strategy](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/BRANCHING.md) +* [List of adopters](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/ADOPTERS.md) +* [Maintainers](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/MAINTAINERS.md) +* [Support](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/SUPPORT.md) +* [Security](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/SECURITY.md) * [License](#license) * [Supported platforms](#supported-platforms) * [Prerequisites](#prerequisites) @@ -50,7 +53,7 @@ The tasks can be executed by running simple playbooks written in yaml syntax. * [Maintanence](#maintanence) ## License -Ansible collection for PowerScale is released and licensed under the GPL-3.0 license. See [LICENSE](https://github.com/dell/ansible-powerscale/blob/2.1.0/LICENSE) for the full terms. Ansible modules and modules utilities that are part of the Ansible collection for PowerScale are released and licensed under the Apache 2.0 license. See [MODULE-LICENSE](https://github.com/dell/ansible-powerscale/blob/2.1.0/MODULE-LICENSE) for the full terms. +Ansible collection for PowerScale is released and licensed under the GPL-3.0 license. See [LICENSE](https://github.com/dell/ansible-powerscale/blob/2.2.0/LICENSE) for the full terms. Ansible modules and modules utilities that are part of the Ansible collection for PowerScale are released and licensed under the Apache 2.0 license. See [MODULE-LICENSE](https://github.com/dell/ansible-powerscale/blob/2.2.0/MODULE-LICENSE) for the full terms. ## Supported platforms * Dell PowerScale OneFS versions 9.3.x, 9.4.x and 9.5.x @@ -60,44 +63,47 @@ This table provides information about the software prerequisites for the Ansible | **Ansible Modules** | **OneFS Version** | **Python version** | **Python SDK version** | **Ansible** | |---------------------|-----------------------|--------------------|----------------------------|--------------------------| -| v2.1.0 | 9.3.x
9.4.x
9.5.x | 3.9
3.10
3.11 | 9.3.0
9.4.0
9.5.0 | 2.13
2.14
2.15 | +| v2.2.0 | 9.3.x
9.4.x
9.5.x | 3.9
3.10
3.11 | 9.3.0
9.4.0
9.5.0 | 2.13
2.14
2.15 | # List of Ansible modules for Dell PowerScale - * [File System Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/filesystem.rst) - * [Access Zone Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/accesszone.rst) - * [User Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/user.rst) - * [Group Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/group.rst) - * [Snapshot Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/snapshot.rst) - * [Snapshot Schedule Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/snapshotschedule.rst) - * [NFS Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/nfs.rst) - * [SMB Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/smb.rst) - * [Smart Quota Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/smartquota.rst) - * [Info Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/info.rst) - * [Active Directory Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/ads.rst) - * [LDAP Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/ldap.rst) - * [Node Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/node.rst) - * [SyncIQ Policy Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/synciqpolicy.rst) - * [SyncIQ Jobs Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/synciqjob.rst) - * [SyncIQ Performance Rules Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/synciqrules.rst) - * [SyncIQ Reports Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/synciqreports.rst) - * [SyncIQ Target Reports Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/synciqtargetreports.rst) - * [Groupnet Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/groupnet.rst) - * [Subnet Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/subnet.rst) - * [Network Pool Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/networkpool.rst) - * [Network Rule Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/networkrule.rst) - * [NFS Alias Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/nfs_alias.rst) - * [Settings Module](https://github.com/dell/ansible-powerscale/tree/2.1.0/docs/modules/settings.rst) - * [Network Setting Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/networksettings.rst) - * [Smart Pool Setting Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/smartpoolsettings.rst) - * [Filepool Policy Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/filepoolpolicy.rst) - * [Storagepool Tier Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/storagepooltier.rst) - * [SMB File Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/smb_file.rst) - * [User Mapping Rule Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/user_mapping_rule.rst) - * [S3 Bucket Module](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/modules/s3_bucket.rst) + * [File System Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/filesystem.rst) + * [Access Zone Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/accesszone.rst) + * [User Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/user.rst) + * [Group Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/group.rst) + * [Snapshot Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/snapshot.rst) + * [Snapshot Schedule Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/snapshotschedule.rst) + * [NFS Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/nfs.rst) + * [SMB Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/smb.rst) + * [Smart Quota Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/smartquota.rst) + * [Info Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/info.rst) + * [Active Directory Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/ads.rst) + * [LDAP Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/ldap.rst) + * [Node Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/node.rst) + * [SyncIQ Policy Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/synciqpolicy.rst) + * [SyncIQ Jobs Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/synciqjob.rst) + * [SyncIQ Performance Rules Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/synciqrules.rst) + * [SyncIQ Reports Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/synciqreports.rst) + * [SyncIQ Target Reports Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/synciqtargetreports.rst) + * [Groupnet Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/groupnet.rst) + * [Subnet Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/subnet.rst) + * [Network Pool Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/networkpool.rst) + * [Network Rule Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/networkrule.rst) + * [NFS Alias Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/nfs_alias.rst) + * [Settings Module](https://github.com/dell/ansible-powerscale/tree/2.2.0/docs/modules/settings.rst) + * [Network Setting Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/networksettings.rst) + * [Smart Pool Setting Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/smartpoolsettings.rst) + * [Filepool Policy Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/filepoolpolicy.rst) + * [Storagepool Tier Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/storagepooltier.rst) + * [SMB File Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/smb_file.rst) + * [User Mapping Rule Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/user_mapping_rule.rst) + * [S3 Bucket Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/s3_bucket.rst) + * [NFS Default Settings Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/nfs_default_settings.rst) + * [NFS Global Settings Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/nfs_global_settings.rst) + * [NFS Zone Settings Module](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/modules/nfs_zone_settings.rst) ## Installation and execution of Ansible modules for Dell PowerScale -The installation and execution steps of Ansible modules for Dell PowerScale can be found [here](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/INSTALLATION.md). +The installation and execution steps of Ansible modules for Dell PowerScale can be found [here](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/INSTALLATION.md). ## Maintenance Ansible Modules for Dell Technologies PowerScale deprecation cycle is aligned with [Ansible](https://docs.ansible.com/ansible/latest/dev_guide/module_lifecycle.html). diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 6e51bfa6..aac32e94 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -169,7 +169,7 @@ releases: - Added support to create a user using user_id. - Added support to list SMB open files through Info module. - Added support to update the password of the user. - - Enabled the path paramter of Smart Quota module to be consistent with other + - Enabled the path parameter of Smart Quota module to be consistent with other modules. modules: - description: Find and close SMB open files on a PowerScale Storage system. @@ -179,11 +179,13 @@ releases: 2.0.0: changes: minor_changes: - - Added support for ignoring unresolvable hosts for NFS Export. - - Added support for everyone user in filesystem module. - - Added support for listing LDAP auth providers and user mapping rules in Info module. - Added support for PowerScale OneFS 9.5 Islander release. - - For the execution of the PowerScale Ansible modules, python library "isilon-sdk" needs to be installed. + - Added support for everyone user in filesystem module. + - Added support for ignoring unresolvable hosts for NFS Export. + - Added support for listing LDAP auth providers and user mapping rules in Info + module. + - For the execution of the PowerScale Ansible modules, python library "isilon-sdk" + needs to be installed. modules: - description: Create, modify, get details, and delete a user mapping rule. name: user_mapping_rule @@ -192,11 +194,30 @@ releases: 2.1.0: changes: minor_changes: - - Added support for deleting an access zone and reordering the authentication providers in access zone module. - - Added support for service principal names(SPN) in AD module. - Added support for SmartConnect zone alaises(DNS names) in network pool module. + - Added support for deleting an access zone and reordering the authentication + providers in access zone module. + - Added support for service principal names(SPN) in AD module. modules: - description: Create, modify, get details and delete an S3 bucket. name: s3_bucket namespace: '' release_date: '2023-07-31' + 2.2.0: + changes: + minor_changes: + - Added support for listing NFS default settings, NFS global settings and NFS + zone settings in Info module. + - Added support for specifying the users and groups to which non-root and root + clients are mapped in nfs module. + modules: + - description: Get details and modify NFS default settings. + name: nfs_default_settings + namespace: '' + - description: Get details and modify NFS global settings. + name: nfs_global_settings + namespace: '' + - description: Get details and modify NFS zone settings. + name: nfs_zone_settings + namespace: '' + release_date: '2023-08-31' diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 671ea646..d28c21ae 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -10,7 +10,7 @@ You may obtain a copy of the License at # How to contribute -Become one of the contributors to this project! We thrive to build a welcoming and open community for anyone who wants to use the project or contribute to it. There are just a few small guidelines you need to follow. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](https://github.com/dell/ansible-powerscale/blob/1.9.0/docs/CODE_OF_CONDUCT.md). +Become one of the contributors to this project! We thrive to build a welcoming and open community for anyone who wants to use the project or contribute to it. There are just a few small guidelines you need to follow. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/CODE_OF_CONDUCT.md). ## Table of contents @@ -76,7 +76,7 @@ Triage helps ensure that issues resolve quickly by: If you don't have the knowledge or time to code, consider helping with _issue triage_. The Ansible modules for Dell PowerScale community will thank you for saving them time by spending some of yours. -Read more about the ways you can [Triage issues](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/ISSUE_TRIAGE.md). +Read more about the ways you can [Triage issues](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/ISSUE_TRIAGE.md). ## Your first contribution @@ -89,7 +89,7 @@ When you're ready to contribute, it's time to create a pull request. ## Branching -* [Branching Strategy for Ansible modules for Dell PowerScale](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/BRANCHING.md) +* [Branching Strategy for Ansible modules for Dell PowerScale](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/BRANCHING.md) ## Signing your commits @@ -144,7 +144,7 @@ Make sure that the title for your pull request uses the same format as the subje ### Quality gates for pull requests -GitHub Actions are used to enforce quality gates when a pull request is created or when any commit is made to the pull request. These GitHub Actions enforce our minimum code quality requirement for any code that get checked into the repository. If any of the quality gates fail, it is expected that the contributor will look into the check log, understand the problem and resolve the issue. If help is needed, please feel free to reach out the maintainers of the project for [support](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/SUPPORT.md). +GitHub Actions are used to enforce quality gates when a pull request is created or when any commit is made to the pull request. These GitHub Actions enforce our minimum code quality requirement for any code that get checked into the repository. If any of the quality gates fail, it is expected that the contributor will look into the check log, understand the problem and resolve the issue. If help is needed, please feel free to reach out the maintainers of the project for [support](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/SUPPORT.md). #### Code sanitization diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 87ccf03f..8e7cf9fb 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -32,7 +32,7 @@ Use this procedure to install SDK: #### Offline installation of collections 1. Download the latest tar build from either of the available distribution channels [Ansible Galaxy](https://galaxy.ansible.com/dellemc/powerscale) /[Automation Hub](https://console.redhat.com/ansible/automation-hub/repo/published/dellemc/powerscale) and use this command to install the collection anywhere in your system: - ansible-galaxy collection install dellemc-powerscale-2.1.0.tar.gz -p + ansible-galaxy collection install dellemc-powerscale-2.2.0.tar.gz -p 2. Set the environment variable: @@ -59,7 +59,7 @@ Use this procedure to install SDK: ## Ansible modules execution -The Ansible server must be configured with Python library for OneFS to run the Ansible playbooks. The [Documents](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs) provide information on different Ansible modules along with their functions and syntax. The parameters table in the Product Guide provides information on various parameters which need to be configured before running the modules. +The Ansible server must be configured with Python library for OneFS to run the Ansible playbooks. The [Documents](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs) provide information on different Ansible modules along with their functions and syntax. The parameters table in the Product Guide provides information on various parameters which need to be configured before running the modules. ## SSL certificate validation diff --git a/docs/ISSUE_TRIAGE.md b/docs/ISSUE_TRIAGE.md index 1c39bde0..5f47983b 100644 --- a/docs/ISSUE_TRIAGE.md +++ b/docs/ISSUE_TRIAGE.md @@ -43,7 +43,7 @@ Should explain what happened, what was expected and how to reproduce it together - Ansible Version: [e.g. 2.15] - Python Version [e.g. 3.11] - - Ansible modules for Dell PowerScale Version: [e.g. 2.1.0] + - Ansible modules for Dell PowerScale Version: [e.g. 2.2.0] - PowerScale SDK version: [e.g. isilon-sdk] - Any other additional information... diff --git a/docs/MAINTAINER_GUIDE.md b/docs/MAINTAINER_GUIDE.md index 4d741af3..40501734 100644 --- a/docs/MAINTAINER_GUIDE.md +++ b/docs/MAINTAINER_GUIDE.md @@ -27,7 +27,7 @@ If a candidate is approved, a Maintainer contacts the candidate to invite them t ## Maintainer policies * Lead by example -* Follow the [Code of Conduct](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/CODE_OF_CONDUCT.md) and the guidelines in the [Contributing](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/CONTRIBUTING.md) and [Committer](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/COMMITTER_GUIDE.md) guides +* Follow the [Code of Conduct](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/CODE_OF_CONDUCT.md) and the guidelines in the [Contributing](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/CONTRIBUTING.md) and [Committer](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/COMMITTER_GUIDE.md) guides * Promote a friendly and collaborative environment within our community * Be actively engaged in discussions, answering questions, updating defects, and reviewing pull requests * Criticize code, not people. Ideally, tell the contributor a better way to do what they need. diff --git a/docs/Release Notes.md b/docs/Release Notes.md index 75fdb1ce..e47ca08d 100644 --- a/docs/Release Notes.md +++ b/docs/Release Notes.md @@ -1,6 +1,6 @@ **Ansible Modules for Dell Technologies PowerScale** ========================================= -### Release notes 2.1.0 +### Release notes 2.2.0 > © 2022 Dell Inc. or its subsidiaries. All rights reserved. Dell > and other trademarks are trademarks of Dell Inc. or its @@ -27,7 +27,7 @@ Table 1. Revision history | Revision | Date | Description | |----------|---------------|-----------------------------------------------------------| -| 01 | July 2023 | Ansible Modules for Dell PowerScale 2.1.0 | +| 01 | August 2023 | Ansible Modules for Dell PowerScale 2.2.0 | Product description @@ -68,16 +68,18 @@ New Features and Enhancements --------------------------- This section describes the features of the Ansible Modules for Dell PowerScale for this release. -The Ansible Modules for Dell PowerScale release 2.1.0 supports the following features: +The Ansible Modules for Dell PowerScale release 2.2.0 supports the following features: -- The S3 bucket module supports this functionality: - - Create, modify, get details and delete an S3 bucket. -- The Access zone module supports this functionality: - - Added support for deleting an access zone and reordering the authentication providers. -- The AD module supports this functionality: - - Added support for service principal names (SPN). -- The Network Pool module supports this functionality: - - Added support for SmartConnect zone alaises(DNS names) +- The NFS default settings module supports this functionality: + - Get details and modify NFS default settings. +- The NFS global settings module supports this functionality: + - Get details and modify NFS global settings. +- The NFS zone settings module supports this functionality: + - Get details and modify NFS zone settings. +- The NFS module supports this functionality: + - Added support for specifying the users and groups to which non-root and root clients are mapped. +- The Info module supports this functionality: + - Added support for getting details of NFS default settings, NFS global settings and NFS zone settings. Known issues ------------ @@ -109,7 +111,6 @@ This section lists the limitations in this release of Ansible Modules for Dell P - Filesystems - Only directory quotas are supported but not user or group quotas. - - Modification of include_snap_data flag is not supported. - NFS Export - If there are multiple exports present with the same path in an access zone, operations on such exports fail. @@ -128,7 +129,7 @@ This section lists the limitations in this release of Ansible Modules for Dell P Software media, organization, and files ----------- The software package is available for download from the [Ansible Modules -for PowerScale GitHub](https://github.com/dell/ansible-powerscale/tree/2.1.0) page. +for PowerScale GitHub](https://github.com/dell/ansible-powerscale/tree/2.2.0) page. Additional resources -------------------- diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 4f3840d2..942be679 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -12,7 +12,7 @@ You may obtain a copy of the License at The Ansible modules for Dell PowerScale repository is inspected for security vulnerabilities via blackduck scans and static code analysis. -In addition to this, there are various security checks that get executed against a branch when a pull request is created/updated. Please refer to [pull request](https://github.com/dell/ansible-powerscale/blob/2.1.0/docs/CONTRIBUTING.md#Pull-requests) for more information. +In addition to this, there are various security checks that get executed against a branch when a pull request is created/updated. Please refer to [pull request](https://github.com/dell/ansible-powerscale/blob/2.2.0/docs/CONTRIBUTING.md#Pull-requests) for more information. ## Reporting a vulnerability diff --git a/docs/modules/info.rst b/docs/modules/info.rst index 5753730d..2780e10a 100644 --- a/docs/modules/info.rst +++ b/docs/modules/info.rst @@ -20,6 +20,12 @@ Get list of network groupnets, network pools for all access zones or a specific Get list of user mapping rules, ldap providers of the PowerScale cluster. +Get NFS zone settings details of the PowerScale cluster. + +Get NFS default settings details of the PowerScale cluster. + +Get NFS global settings details of the PowerScale cluster. + Requirements @@ -110,6 +116,10 @@ Parameters LDAPs - ``ldap``. + NFS zone settings - ``nfs_zone_settings``. + + NFS default settings - ``nfs_default_settings``. + The list of *attributes*, *access_zones* and *nodes* is for the entire PowerScale cluster. The list of providers for the entire PowerScale cluster. @@ -425,6 +435,33 @@ Examples - ldap scope: "effective" + - name: Get the NFS zone settings of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + gather_subset: + - nfs_zone_settings + + - name: Get the NFS default settings of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + gather_subset: + - nfs_default_settings + + - name: Get the NFS global settings of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + gather_subset: + - nfs_global_settings + Return Values @@ -659,6 +696,84 @@ NfsExports (When C(nfs_exports) is in a given I(gather_subset), list, [{'id': 20 +NfsZoneSettings (When C(nfs_zone_settings) is in a given I(gather_subset), dict, {'nfsv4_allow_numeric_ids': True, 'nfsv4_domain': 'sample.com', 'nfsv4_no_domain': True, 'nfsv4_no_domain_uids': True, 'nfsv4_no_names': True, 'nfsv4_replace_domain': True, 'zone': 'System'}) + Details of NFS zone settings. + + + nfsv4_allow_numeric_ids (, bool, ) + If ``true``, sends owners and groups as UIDs and GIDs when look up fails or if the *nfsv4_no_names* property is set to 1. + + + nfsv4_domain (, str, ) + Specifies the domain through which users and groups are associated. + + + nfsv4_no_domain (, bool, ) + If ``true``, sends owners and groups without a domain name. + + + nfsv4_no_domain_uids (, bool, ) + If ``true``, sends UIDs and GIDs without a domain name. + + + nfsv4_no_names (, bool, ) + If ``true``, sends owners and groups as UIDs and GIDs. + + + nfsv4_replace_domain (, bool, ) + If ``true``, replaces the owner or group domain with an NFS domain name. + + + zone (, str, ) + Specifies the access zone in which the NFS zone settings apply. + + + +NfsGlobalSettings (When C(nfs_global_settings) is in a given I(gather_subset), dict, {'nfsv3_enabled': False, 'nfsv3_rdma_enabled': True, 'nfsv40_enabled': True, 'nfsv41_enabled': True, 'nfsv42_enabled': False, 'nfsv4_enabled': True, 'rpc_maxthreads': 20, 'rpc_minthreads': 17, 'rquota_enabled': True, 'service': True}) + Details of NFS global settings. + + + nfsv3_enabled (, bool, ) + Whether NFSv3 protocol is enabled/disabled. + + + nfsv3_rdma_enabled (, bool, ) + Whether rdma is enabled for NFSv3 protocol. + + + nfsv40_enabled (, bool, ) + Whether version 0 of NFSv4 protocol is enabled/disabled. + + + nfsv41_enabled (, bool, ) + Whether version 1 of NFSv4 protocol is enabled/disabled. + + + nfsv42_enabled (, bool, ) + Whether version 2 of NFSv4 protocol is enabled/disabled. + + + nfsv4_enabled (, bool, ) + Whether NFSv4 protocol is enabled/disabled. + + + rpc_maxthreads (, int, ) + Specifies the maximum number of threads in the nfsd thread pool. + + + rpc_minhreads (, int, ) + Specifies the minimum number of threads in the nfsd thread pool. + + + rquota_enabled (, bool, ) + Whether the rquota protocol is enabled/disabled. + + + service (, bool, ) + Whether the NFS service is enabled/disabled. + + + NodePools (When C(node_pools) is in a given I(gather_subset), list, [{'can_disable_l3': True, 'can_enable_l3': True, 'health_flags': ['missing_drives'], 'id': 1, 'l3': True, 'l3_status': 'l3', 'lnns': [1], 'manual': False, 'name': 's210_6.9tb_1.6tb-ssd_64gb', 'node_type_ids': [1], 'protection_policy': '+2d:1n', 'tier': None, 'transfer_limit_pct': 90, 'transfer_limit_state': 'default', 'usage': {}}]) List of the Node pools. @@ -942,6 +1057,131 @@ Users (When C(users) is in a given I(gather_subset), list, [{'users': [{'dn': 'C +nfs_default_settings (always, dict, {'map_root': {'enabled': True, 'primary_group': {'id': 'None', 'name': 'None', 'type': 'None'}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': 'None', 'type': 'None'}}, 'map_non_root': {'enabled': False, 'primary_group': {'id': 'None', 'name': 'None', 'type': 'None'}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': 'None', 'type': 'None'}}, 'map_failure': {'enabled': False, 'primary_group': {'id': 'None', 'name': 'None', 'type': 'None'}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': 'None', 'type': 'None'}}, 'name_max_size': 255, 'block_size': 8192, 'commit_asynchronous': False, 'directory_transfer_size': 131072, 'read_transfer_max_size': 1048576, 'read_transfer_multiple': 512, 'read_transfer_size': 131072, 'setattr_asynchronous': False, 'write_datasync_action': 'DATASYNC', 'write_datasync_reply': 'DATASYNC', 'write_filesync_action': 'FILESYNC', 'write_filesync_reply': 'FILESYNC', 'write_transfer_max_size': 1048576, 'write_transfer_multiple': 512, 'write_transfer_size': 524288, 'write_unstable_action': 'UNSTABLE', 'write_unstable_reply': 'UNSTABLE', 'max_file_size': 9223372036854775807, 'readdirplus': True, 'return_32bit_file_ids': False, 'can_set_time': True, 'encoding': 'DEFAULT', 'map_lookup_uid': False, 'symlinks': True, 'time_delta': '1e-09', 'zone': 'sample-zone'}) + The NFS default settings. + + + map_root (, dict, ) + Mapping of incoming root users to a specific user and/or group ID. + + + map_non_root (, dict, ) + Mapping of non-root users to a specific user and/or group ID. + + + map_failure (, dict, ) + Mapping of users to a specific user and/or group ID after a failed auth attempt. + + + name_max_size (, dict, ) + Specifies the reported maximum length of a file name. This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + block_size (, dict, ) + Specifies the block size returned by the NFS statfs procedure. + + + directory_transfer_size (, dict, ) + Specifies the preferred size for directory read operations. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + read_transfer_max_size (, dict, ) + Specifies the maximum buffer size that clients should use on NFS read requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + read_transfer_multiple (, dict, ) + Specifies the preferred multiple size for NFS read requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + read_transfer_size (, dict, ) + Specifies the preferred size for NFS read requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_transfer_max_size (, dict, ) + Specifies the maximum buffer size that clients should use on NFS write requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_transfer_multiple (, dict, ) + Specifies the preferred multiple size for NFS write requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_transfer_size (, dict, ) + Specifies the preferred multiple size for NFS write requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + max_file_size (, dict, ) + Specifies the maximum file size for any file accessed from the export. This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + security_flavors (, list, ) + Specifies the authentication types that are supported for this export. + + + commit_asynchronous (, bool, ) + True if NFS commit requests execute asynchronously. + + + setattr_asynchronous (, bool, ) + True if set attribute operations execute asynchronously. + + + readdirplus (, bool, ) + True if 'readdirplus' requests are enabled. Enabling this property might improve network performance and is only available for NFSv3. + + + return_32bit_file_ids (, bool, ) + Limits the size of file identifiers returned by NFSv3+ to 32-bit values (may require remount). + + + can_set_time (, bool, ) + True if the client can set file times through the NFS set attribute request. This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + map_lookup_uid (, bool, ) + True if incoming user IDs (UIDs) are mapped to users in the OneFS user database. When set to False, incoming UIDs are applied directly to file operations. + + + symlinks (, bool, ) + True if symlinks are supported. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_datasync_action (, str, ) + Specifies the synchronization type for data sync action. + + + write_datasync_reply (, str, ) + Specifies the synchronization type for data sync reply. + + + write_filesync_action (, str, ) + Specifies the synchronization type for file sync action. + + + write_filesync_reply (, str, ) + Specifies the synchronization type for file sync reply. + + + write_unstable_action (, str, ) + Specifies the synchronization type for unstable action. + + + write_unstable_reply (, str, ) + Specifies the synchronization type for unstable reply. + + + encoding (, str, ) + Specifies the default character set encoding of the clients connecting to the export, unless otherwise specified. + + + time_delta (, dict, ) + Specifies the resolution of all time values that are returned to the clients. + + + zone (, str, ) + The zone to which the NFS default settings apply. + + + @@ -959,4 +1199,6 @@ Authors - Spandita Panigrahi(@panigs7) - Pavan Mudunuri(@Pavan-Mudunuri) - Ananthu S Kuttattu(@kuttattz) +- Bhavneet Sharma(@Bhavneet-Sharma) +- Trisha Datta(@trisha-dell) diff --git a/docs/modules/nfs.rst b/docs/modules/nfs.rst index fad4d913..1a08c4ec 100644 --- a/docs/modules/nfs.rst +++ b/docs/modules/nfs.rst @@ -119,6 +119,66 @@ Parameters Does not present an error condition on unresolvable hosts when creating or modifying an export. + map_root (optional, dict, None) + Specifies the users and groups to which non-root and root clients are mapped. + + + enabled (optional, bool, True) + True if the user mapping is applied. + + + user (optional, str, None) + Specifies the persona name. + + + primary_group (optional, str, None) + Specifies the primary group name. + + + secondary_groups (optional, list, None) + Specifies the secondary groups. + + + name (True, str, None) + Specifies the group name. + + + state (optional, str, present) + Specifies the group state. + + + + + map_non_root (optional, dict, None) + Specifies the users and groups to which non-root and root clients are mapped. + + + enabled (optional, bool, True) + True if the user mapping is applied. + + + user (optional, str, None) + Specifies the persona name. + + + primary_group (optional, str, None) + Specifies the primary group name. + + + secondary_groups (optional, list, None) + Specifies the secondary groups. + + + name (True, str, None) + Specifies the group name. + + + state (optional, str, present) + Specifies the group state. + + + + onefs_host (True, str, None) IP address or FQDN of the PowerScale cluster. @@ -250,6 +310,39 @@ Examples read_only: false state: 'present' + - name: Modify map_root and map_non_root + dellemc.powerscale.nfs: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + path: "" + access_zone: "{{access_zone}}" + map_root: + user: "root" + primary_group: "root" + secondary_groups: + - name: "group_test" + map_non_root: + user: "root" + primary_group: "root" + secondary_groups: + - name: "group_test" + state: "absent" + state: 'present' + + - name: Disable map_root + dellemc.powerscale.nfs: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + path: "" + access_zone: "{{access_zone}}" + map_root: + enabled: false + state: 'present' + - name: Delete NFS Export dellemc.powerscale.nfs: onefs_host: "{{onefs_host}}" @@ -269,7 +362,7 @@ changed (always, bool, false) A boolean indicating if the task had to make changes. -NFS_export_details (always, complex, {'all_dir': 'false', 'block_size': 8192, 'clients': 'None', 'id': 9324, 'read_only_client': ['x.x.x.x'], 'security_flavors': ['unix', 'krb5'], 'zone': 'System'}) +NFS_export_details (always, complex, {'all_dir': 'false', 'block_size': 8192, 'clients': 'None', 'id': 9324, 'read_only_client': ['x.x.x.x'], 'security_flavors': ['unix', 'krb5'], 'zone': 'System', 'map_root': {'enabled': True, 'primary_group': {'id': 'GROUP:group1', 'name': None, 'type': None}, 'secondary_groups': [], 'user': {'id': 'USER:user', 'name': None, 'type': None}}, 'map_non_root': {'enabled': False, 'primary_group': {'id': None, 'name': None, 'type': None}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': None, 'type': None}}}) The updated NFS Export details. @@ -313,6 +406,68 @@ NFS_export_details (always, complex, {'all_dir': 'false', 'block_size': 8192, 'c Description for the export. + map_root (, complex, ) + Specifies the users and groups to which non-root and root clients are mapped. + + + enabled (, bool, ) + True if the user mapping is applied. + + + user (, complex, ) + Specifies the persona name. + + + id (, str, ) + Specifies the persona name. + + + + primary_group (, complex, ) + Specifies the primary group. + + + id (, str, ) + Specifies the primary group name. + + + + secondary_groups (, list, ) + Specifies the secondary groups. + + + + map_non_root (, complex, ) + Specifies the users and groups to which non-root and root clients are mapped. + + + enabled (, bool, ) + True if the user mapping is applied. + + + user (, complex, ) + Specifies the persona details. + + + id (, str, ) + Specifies the persona name. + + + + primary_group (, complex, ) + Specifies the primary group details. + + + id (, str, ) + Specifies the primary group name. + + + + secondary_groups (, list, ) + Specifies the secondary groups details. + + + diff --git a/docs/modules/nfs_default_settings.rst b/docs/modules/nfs_default_settings.rst new file mode 100644 index 00000000..1e60d07c --- /dev/null +++ b/docs/modules/nfs_default_settings.rst @@ -0,0 +1,603 @@ +.. _nfs_default_settings_module: + + +nfs_default_settings -- Manage NFS default settings on a PowerScale Storage System +================================================================================== + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- + +Managing NFS default settings on an PowerScale system includes getting details of an NFS default settings and modifying different attributes of the NFS default settings. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- A Dell PowerScale Storage system. +- Ansible-core 2.13 or later. +- Python 3.9, 3.10 or 3.11. + + + +Parameters +---------- + + map_root (False, dict, None) + User and group mapping. + + Map incoming root users to a specific user and/or group ID. + + + enabled (optional, bool, True) + Indicates if user mapping is enabled or not. + + True if the user mapping is applied. + + + primary_group (False, str, None) + Specifies name of the primary group. + + + secondary_groups (False, list, None) + Specifies name and state of the secondary groups. + + + name (True, str, None) + Name of the group. + + + state (optional, str, present) + State of the secondary group. + + + + user (False, str, None) + Specifies name of the user. + + + + map_non_root (False, dict, None) + User and group mapping. + + Map non-root users to a specific user and/or group ID. + + + enabled (optional, bool, True) + Indicates if user mapping is enabled or not. + + True if the user mapping is applied. + + + primary_group (False, str, None) + Specifies name of the primary group. + + + secondary_groups (False, list, None) + Specifies name and state of the secondary groups. + + + name (True, str, None) + Name of the group. + + + state (optional, str, present) + State of the secondary group. + + + + user (False, str, None) + Specifies name of the user. + + + + map_failure (False, dict, None) + User and group mapping. + + Map users to a specific user and/or group ID after a failed auth attempt. + + + enabled (optional, bool, True) + Indicates if user mapping is enabled or not. + + True if the user mapping is applied. + + + primary_group (False, str, None) + Specifies name of the primary group. + + + secondary_groups (False, list, None) + Specifies name and state of the secondary groups. + + + name (True, str, None) + Name of the group. + + + state (optional, str, present) + State of the secondary group. + + + + user (False, str, None) + Specifies name of the user. + + + + file_name_max_size (False, dict, None) + Specifies the reported maximum length of a file name. + + This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + block_size (False, dict, None) + Specifies the block size returned by the NFS statfs procedure. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + directory_transfer_size (False, dict, None) + Specifies the preferred size for directory read operations. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + read_transfer_max_size (False, dict, None) + Specifies the maximum buffer size that clients should use on NFS read requests. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + read_transfer_multiple (False, dict, None) + Specifies the preferred multiple size for NFS read requests. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + read_transfer_size (False, dict, None) + Specifies the preferred size for NFS read requests. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + write_transfer_max_size (False, dict, None) + Specifies the maximum buffer size that clients should use on NFS write requests. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + write_transfer_multiple (False, dict, None) + Specifies the preferred multiple size for NFS write requests. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + write_transfer_size (False, dict, None) + Specifies the preferred multiple size for NFS write requests. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + max_file_size (False, dict, None) + Specifies the maximum file size for any file accessed from the export. + + This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + size_value (True, int, None) + Size value. + + + size_unit (True, str, None) + Unit for the size value. + + + + security_flavors (False, list, None) + Specifies the authentication types that are supported for this export. + + + commit_asynchronous (False, bool, None) + True if NFS commit requests execute asynchronously. + + + setattr_asynchronous (False, bool, None) + True if set attribute operations execute asynchronously. + + + readdirplus (False, bool, None) + True if 'readdirplus' requests are enabled. + + Enabling this property might improve network performance and is only available for NFSv3. + + + return_32bit_file_ids (False, bool, None) + Limits the size of file identifiers returned by NFSv3+ to 32-bit values (may require remount). + + + can_set_time (False, bool, None) + True if the client can set file times through the NFS set attribute request. + + This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + map_lookup_uid (False, bool, None) + True if incoming user IDs (UIDs) are mapped to users in the OneFS user database. + + When set to False, incoming UIDs are applied directly to file operations. + + + symlinks (False, bool, None) + True if symlinks are supported. + + This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_datasync_action (optional, str, None) + Specifies the synchronization type for datasync action. + + + write_datasync_reply (optional, str, None) + Specifies the synchronization type for datasync reply. + + + write_filesync_action (optional, str, None) + Specifies the synchronization type for filesync action. + + + write_filesync_reply (optional, str, None) + Specifies the synchronization type for filesync reply. + + + write_unstable_action (optional, str, None) + Specifies the synchronization type for unstable action. + + + write_unstable_reply (optional, str, None) + Specifies the synchronization type for unstable reply. + + + encoding (optional, str, None) + Specifies the default character set encoding of the clients connecting to the export, unless otherwise specified. + + + time_delta (False, dict, None) + Specifies the resolution of all time values that are returned to the clients. + + + time_value (True, float, None) + Time value. + + + time_unit (True, str, None) + Unit for the time value. + + + + access_zone (optional, str, System) + The zone to which the NFS default settings apply. + + + onefs_host (True, str, None) + IP address or FQDN of the PowerScale cluster. + + + port_no (False, str, 8080) + Port number of the PowerScale cluster.It defaults to 8080 if not specified. + + + verify_ssl (True, bool, None) + boolean variable to specify whether to validate SSL certificate or not. + + ``true`` - indicates that the SSL certificate should be verified. + + ``false`` - indicates that the SSL certificate should not be verified. + + + api_user (True, str, None) + username of the PowerScale cluster. + + + api_password (True, str, None) + the password of the PowerScale cluster. + + + + + +Notes +----- + +.. note:: + - The *check_mode* is supported. + - The modules present in this collection named as 'dellemc.powerscale' are built to support the Dell PowerScale storage platform. + + + + +Examples +-------- + +.. code-block:: yaml+jinja + + + - name: Get NFS default settings + dellemc.powerscale.nfs_default_settings: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + access_zone: "sample-zone" + + - name: Update the NFS default settings + dellemc.powerscale.nfs_default_settings: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "sample-zone" + block_size: + size_value: 5 + size_unit: 'KB' + commit_asynchronous: False + encoding: 'UTF8' + map_root: + enabled: true + primary_group: 'test_group_1' + secondary_groups: + - name: 'test_group_2' + - name: 'test_group_3' + state: 'absent' + user: 'test_user' + map_non_root: + enabled: true + primary_group: 'test_non_root_group' + secondary_groups: + - name: 'test_non_root_group_2' + - name: 'test_non_root_group_3' + state: 'absent' + user: 'test_non_root_user' + readdirplus: true + time_delta: + time_value: 5 + time_unit: 'seconds' + write_filesync_action: 'DATASYNC' + security_flavors: + - unix + - kerberos + + + +Return Values +------------- + +changed (always, bool, false) + A boolean indicating if the task had to make changes. + + +nfs_default_settings (always, dict, {'map_root': {'enabled': True, 'primary_group': {'id': 'None', 'name': 'None', 'type': 'None'}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': 'None', 'type': 'None'}}, 'map_non_root': {'enabled': False, 'primary_group': {'id': 'None', 'name': 'None', 'type': 'None'}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': 'None', 'type': 'None'}}, 'map_failure': {'enabled': False, 'primary_group': {'id': 'None', 'name': 'None', 'type': 'None'}, 'secondary_groups': [], 'user': {'id': 'USER:nobody', 'name': 'None', 'type': 'None'}}, 'name_max_size': 255, 'block_size': 8192, 'commit_asynchronous': False, 'directory_transfer_size': 131072, 'read_transfer_max_size': 1048576, 'read_transfer_multiple': 512, 'read_transfer_size': 131072, 'setattr_asynchronous': False, 'write_datasync_action': 'DATASYNC', 'write_datasync_reply': 'DATASYNC', 'write_filesync_action': 'FILESYNC', 'write_filesync_reply': 'FILESYNC', 'write_transfer_max_size': 1048576, 'write_transfer_multiple': 512, 'write_transfer_size': 524288, 'write_unstable_action': 'UNSTABLE', 'write_unstable_reply': 'UNSTABLE', 'max_file_size': 9223372036854775807, 'readdirplus': True, 'return_32bit_file_ids': False, 'can_set_time': True, 'encoding': 'DEFAULT', 'map_lookup_uid': False, 'symlinks': True, 'time_delta': '1e-09', 'zone': 'sample-zone'}) + The NFS default settings. + + + map_root (, dict, ) + Mapping of incoming root users to a specific user and/or group ID. + + + map_non_root (, dict, ) + Mapping of non-root users to a specific user and/or group ID. + + + map_failure (, dict, ) + Mapping of users to a specific user and/or group ID after a failed auth attempt. + + + name_max_size (, dict, ) + Specifies the reported maximum length of a file name. This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + block_size (, dict, ) + Specifies the block size returned by the NFS statfs procedure. + + + directory_transfer_size (, dict, ) + Specifies the preferred size for directory read operations. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + read_transfer_max_size (, dict, ) + Specifies the maximum buffer size that clients should use on NFS read requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + read_transfer_multiple (, dict, ) + Specifies the preferred multiple size for NFS read requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + read_transfer_size (, dict, ) + Specifies the preferred size for NFS read requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_transfer_max_size (, dict, ) + Specifies the maximum buffer size that clients should use on NFS write requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_transfer_multiple (, dict, ) + Specifies the preferred multiple size for NFS write requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_transfer_size (, dict, ) + Specifies the preferred multiple size for NFS write requests. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + max_file_size (, dict, ) + Specifies the maximum file size for any file accessed from the export. This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + security_flavors (, list, ) + Specifies the authentication types that are supported for this export. + + + commit_asynchronous (, bool, ) + True if NFS commit requests execute asynchronously. + + + setattr_asynchronous (, bool, ) + True if set attribute operations execute asynchronously. + + + readdirplus (, bool, ) + True if 'readdirplus' requests are enabled. Enabling this property might improve network performance and is only available for NFSv3. + + + return_32bit_file_ids (, bool, ) + Limits the size of file identifiers returned by NFSv3+ to 32-bit values (may require remount). + + + can_set_time (, bool, ) + True if the client can set file times through the NFS set attribute request. This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + + + map_lookup_uid (, bool, ) + True if incoming user IDs (UIDs) are mapped to users in the OneFS user database. When set to False, incoming UIDs are applied directly to file operations. + + + symlinks (, bool, ) + True if symlinks are supported. This value is used to advise the client of optimal settings for the server, but is not enforced. + + + write_datasync_action (, str, ) + Specifies the synchronization type for data sync action. + + + write_datasync_reply (, str, ) + Specifies the synchronization type for data sync reply. + + + write_filesync_action (, str, ) + Specifies the synchronization type for file sync action. + + + write_filesync_reply (, str, ) + Specifies the synchronization type for file sync reply. + + + write_unstable_action (, str, ) + Specifies the synchronization type for unstable action. + + + write_unstable_reply (, str, ) + Specifies the synchronization type for unstable reply. + + + encoding (, str, ) + Specifies the default character set encoding of the clients connecting to the export, unless otherwise specified. + + + time_delta (, dict, ) + Specifies the resolution of all time values that are returned to the clients. + + + zone (, str, ) + The zone to which the NFS default settings apply. + + + + + + +Status +------ + + + + + +Authors +~~~~~~~ + +- Ananthu S Kuttattu(@kuttattz) + diff --git a/docs/modules/nfs_global_settings.rst b/docs/modules/nfs_global_settings.rst new file mode 100644 index 00000000..05a90c89 --- /dev/null +++ b/docs/modules/nfs_global_settings.rst @@ -0,0 +1,220 @@ +.. _nfs_global_settings_module: + + +nfs_global_settings -- Manage NFS global settings on a PowerScale Storage System +================================================================================ + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- + +Managing NFS global settings on an PowerScale system includes retrieving details of NFS global settings and modifying NFS global settings. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- A Dell PowerScale Storage system. +- Ansible-core 2.13 or later. +- Python 3.9, 3.10 or 3.11. + + + +Parameters +---------- + + service (optional, bool, None) + Specifies if the NFS service needs to be enabled or not. + + + rpc_maxthreads (optional, int, None) + Specifies the maximum number of threads in the nfsd thread pool. + + + rpc_minthreads (optional, int, None) + Specifies the minimum number of threads in the nfsd thread pool. + + + rquota_enabled (optional, bool, None) + Enable/Disable the rquota protocol. + + + nfsv3 (optional, dict, None) + Enable/disable NFSv3 protocol. + + + nfsv3_enabled (optional, bool, None) + Enable/disable NFSv3 protocol. + + + nfsv3_rdma_enabled (optional, bool, None) + To enable/disable RDMA for NFSv3 protocol. + + + + nfsv4 (optional, dict, None) + Specifies the minor versions of NFSv4 protocol. + + + nfsv4_enabled (optional, bool, None) + Enable/disable all minor versions of NFSv4 protocol. + + + nfsv40_enabled (optional, bool, None) + Enable/disable minor version 0 of NFSv4 protocol. + + + nfsv41_enabled (optional, bool, None) + Enable/disable minor version 1 of NFSv4 protocol. + + + nfsv42_enabled (optional, bool, None) + Enable/disable minor version 2 of NFSv4 protocol. + + + + onefs_host (True, str, None) + IP address or FQDN of the PowerScale cluster. + + + port_no (False, str, 8080) + Port number of the PowerScale cluster.It defaults to 8080 if not specified. + + + verify_ssl (True, bool, None) + boolean variable to specify whether to validate SSL certificate or not. + + ``true`` - indicates that the SSL certificate should be verified. + + ``false`` - indicates that the SSL certificate should not be verified. + + + api_user (True, str, None) + username of the PowerScale cluster. + + + api_password (True, str, None) + the password of the PowerScale cluster. + + + + + +Notes +----- + +.. note:: + - The *check_mode* is supported. + - The modules present in this collection named as 'dellemc.powerscale' are built to support the Dell PowerScale storage platform. + + + + +Examples +-------- + +.. code-block:: yaml+jinja + + + - name: Get NFS global settings + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + + - name: Update service of NFS global settings + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + service: true + nfsv3: + nfsv3_enabled: false + nfsv4: + nfsv40_enabled: true + nfsv41_enabled: true + nfsv42_enabled: false + rpc_minthreads: 17 + rpc_maxthreads: 20 + rquota_enabled: true + + + + +Return Values +------------- + +changed (always, bool, false) + A boolean indicating if the task had to make changes. + + +nfs_global_settings_details (always, complex, {'nfsv3_enabled': False, 'nfsv3_rdma_enabled': True, 'nfsv40_enabled': True, 'nfsv41_enabled': True, 'nfsv42_enabled': False, 'nfsv4_enabled': True, 'rpc_maxthreads': 20, 'rpc_minthreads': 17, 'rquota_enabled': True, 'service': True}) + The updated nfs global settings details. + + + nfsv3_enabled (, bool, ) + Whether NFSv3 protocol is enabled/disabled. + + + nfsv3_rdma_enabled (, bool, ) + Whether rdma is enabled for NFSv3 protocol. + + + nfsv40_enabled (, bool, ) + Whether version 0 of NFSv4 protocol is enabled/disabled. + + + nfsv41_enabled (, bool, ) + Whether version 1 of NFSv4 protocol is enabled/disabled. + + + nfsv42_enabled (, bool, ) + Whether version 2 of NFSv4 protocol is enabled/disabled. + + + nfsv4_enabled (, bool, ) + Whether NFSv4 protocol is enabled/disabled. + + + rpc_maxthreads (, int, ) + Specifies the maximum number of threads in the nfsd thread pool. + + + rpc_minhreads (, int, ) + Specifies the minimum number of threads in the nfsd thread pool. + + + rquota_enabled (, bool, ) + Whether the rquota protocol is enabled/disabled. + + + service (, bool, ) + Whether the NFS service is enabled/disabled. + + + + + + +Status +------ + + + + + +Authors +~~~~~~~ + +- Trisha Datta (@trisha-dell) + diff --git a/docs/modules/nfs_zone_settings.rst b/docs/modules/nfs_zone_settings.rst new file mode 100644 index 00000000..9a01fffe --- /dev/null +++ b/docs/modules/nfs_zone_settings.rst @@ -0,0 +1,179 @@ +.. _nfs_zone_settings_module: + + +nfs_zone_settings -- Manage NFS zone settings on a PowerScale Storage System +============================================================================ + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- + +Managing NFS zone settings on an PowerScale system includes retrieving details and modifying NFS zone settings. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- A Dell PowerScale Storage system. +- Ansible-core 2.13 or later. +- Python 3.9, 3.10 or 3.11. + + + +Parameters +---------- + + access_zone (optional, str, System) + Specifies the access zone in which the NFS zone settings apply. + + + nfsv4_allow_numeric_ids (optional, bool, None) + If ``true``, send owner and groups as UIDs and GIDs when look up fails or *nfsv4_no_names* is set ``rue``. + + + nfsv4_domain (optional, str, None) + Specifies the domain through which users and groups are associated. + + + nfsv4_no_domain (optional, bool, None) + If ``true``, sends owners and groups without a domain name. + + + nfsv4_no_domain_uids (optional, bool, None) + If ``true``, sends UIDs and GIDs without a domain name. + + + nfsv4_no_names (optional, bool, None) + If ``true``, sends owners and groups as UIDs and GIDs. + + + nfsv4_replace_domain (optional, bool, None) + If ``true``, replaces the owner or group domain with an NFS domain name. + + + onefs_host (True, str, None) + IP address or FQDN of the PowerScale cluster. + + + port_no (False, str, 8080) + Port number of the PowerScale cluster.It defaults to 8080 if not specified. + + + verify_ssl (True, bool, None) + boolean variable to specify whether to validate SSL certificate or not. + + ``true`` - indicates that the SSL certificate should be verified. + + ``false`` - indicates that the SSL certificate should not be verified. + + + api_user (True, str, None) + username of the PowerScale cluster. + + + api_password (True, str, None) + the password of the PowerScale cluster. + + + + + +Notes +----- + +.. note:: + - The *check_mode* is supported. + - The modules present in this collection named as 'dellemc.powerscale' are built to support the Dell PowerScale storage platform. + + + + +Examples +-------- + +.. code-block:: yaml+jinja + + + - name: Get NFS zone settings + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + access_zone: "sample-zone" + + - name: Modify NFS zone settings + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + access_zone: "sample-zone" + nfsv4_allow_numeric_ids: true + nfsv4_domain: "example.com" + nfsv4_no_domain: true + nfsv4_no_domain_uids: false + + + +Return Values +------------- + +changed (always, bool, false) + A boolean indicating if the task had to make changes. + + +nfs_zone_settings_details (always, dict, {'nfsv4_allow_numeric_ids': False, 'nfsv4_domain': '', 'nfsv4_no_domain': False, 'nfsv4_no_domain_uids': False, 'nfsv4_no_names': False, 'nfsv4_replace_domain': False, 'zone': 'System'}) + The NFS zone settings details. + + + nfsv4_allow_numeric_ids (, bool, ) + If ``true``, sends owners and groups as UIDs and GIDs when look up fails or if the *nfsv4_no_names* property is set to 1. + + + nfsv4_domain (, str, ) + Specifies the domain through which users and groups are associated. + + + nfsv4_no_domain (, bool, ) + If ``true``, sends owners and groups without a domain name. + + + nfsv4_no_domain_uids (, bool, ) + If ``true``, sends UIDs and GIDs without a domain name. + + + nfsv4_no_names (, bool, ) + If ``true``, sends owners and groups as UIDs and GIDs. + + + nfsv4_replace_domain (, bool, ) + If ``true``, replaces the owner or group domain with an NFS domain name. + + + zone (, str, ) + Specifies the access zone in which the NFS zone settings apply. + + + + + + +Status +------ + + + + + +Authors +~~~~~~~ + +- Bhavneet Sharma(@Bhavneet-Sharma) + diff --git a/galaxy.yml b/galaxy.yml index 7e358d70..85d37463 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -14,7 +14,7 @@ name: powerscale # The version of the collection. # Must be compatible with semantic versioning -version: 2.1.0 +version: 2.2.0 # The path to the Markdown (.md) readme file. # This path is relative to the root of the collection. @@ -65,13 +65,13 @@ tags: [storage] dependencies: {} # The URL of the originating SCM repository -repository: https://github.com/dell/ansible-powerscale/tree/2.1.0 +repository: https://github.com/dell/ansible-powerscale/tree/2.2.0 # The URL to any online docs -documentation: https://github.com/dell/ansible-powerscale/tree/2.1.0/docs +documentation: https://github.com/dell/ansible-powerscale/tree/2.2.0/docs # The URL to the homepage of the collection/project -homepage: https://github.com/dell/ansible-powerscale/tree/2.1.0 +homepage: https://github.com/dell/ansible-powerscale/tree/2.2.0 # The URL to the collection issue tracker issues: https://www.dell.com/community/Automation/bd-p/Automation diff --git a/playbooks/modules/info.yml b/playbooks/modules/info.yml index 51d3c862..74968bfe 100644 --- a/playbooks/modules/info.yml +++ b/playbooks/modules/info.yml @@ -164,3 +164,30 @@ api_password: "{{ api_password }}" gather_subset: - ldap + + - name: Get NFS zone settings details of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{ onefs_host }}" + verify_ssl: "{{ verify_ssl }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + gather_subset: + - nfs_zone_settings + + - name: Get NFS default settings details of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{ onefs_host }}" + verify_ssl: "{{ verify_ssl }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + gather_subset: + - nfs_default_settings + + - name: Get NFS default settings details of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{ onefs_host }}" + verify_ssl: "{{ verify_ssl }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + gather_subset: + - nfs_global_settings diff --git a/playbooks/modules/nfs.yml b/playbooks/modules/nfs.yml index 3d64a5e0..7d5256ea 100644 --- a/playbooks/modules/nfs.yml +++ b/playbooks/modules/nfs.yml @@ -217,6 +217,60 @@ read_only: false state: 'present' + - name: Set map_root and map_non_root + dellemc.powerscale.nfs: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: false + path: "{{ path }}" + access_zone: "{{ access_zone }}" + map_root: + user: "test_user_1" + primary_group: group3 + secondary_groups: + - name: group1 + - name: group2 + state: absent + map_non_root: + enabled: true + user: "test_user" + primary_group: group1 + state: "present" + + - name: Set map_root and map_non_root - Idempotency + dellemc.powerscale.nfs: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: false + path: "{{ path }}" + access_zone: "{{ access_zone }}" + map_root: + user: "test_user_1" + primary_group: group3 + secondary_groups: + - name: group1 + - name: group2 + state: absent + map_non_root: + enabled: true + user: "test_user" + primary_group: group1 + state: "present" + + - name: Disable map_non_root + dellemc.powerscale.nfs: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: false + path: "{{ path }}" + access_zone: "{{ access_zone }}" + map_non_root: + enabled: false + state: "present" + - name: Delete NFS Export dellemc.powerscale.nfs: onefs_host: "{{ onefs_host }}" diff --git a/playbooks/modules/nfs_default_settings.yml b/playbooks/modules/nfs_default_settings.yml new file mode 100644 index 00000000..3a402fab --- /dev/null +++ b/playbooks/modules/nfs_default_settings.yml @@ -0,0 +1,48 @@ +--- +- name: NFS default settings module on PowerScale + hosts: localhost + connection: local + vars: + onefs_host: "10.**.**.**" + api_user: "***" + api_password: "****" + verify_ssl: false + access_zone: "sample-zone" + + tasks: + - name: Get details of NFS default settings + dellemc.powerscale.nfs_default_settings: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "{{ access_zone }}" + + - name: Update the NFS default settings + dellemc.powerscale.nfs_default_settings: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "{{ access_zone }}" + block_size: + size_value: 5 + size_unit: 'KB' + commit_asynchronous: false + encoding: 'UTF8' + map_root: + enabled: true + primary_group: 'group_1' + secondary_groups: + - name: 'group_2' + - name: 'group_3' + state: 'absent' + user: 'ntpd' + readdirplus: true + time_delta: + time_value: 5 + time_unit: 'seconds' + write_filesync_action: 'DATASYNC' + security_flavors: + - unix + - kerberos diff --git a/playbooks/modules/nfs_global_settings.yml b/playbooks/modules/nfs_global_settings.yml new file mode 100644 index 00000000..6ecd96ae --- /dev/null +++ b/playbooks/modules/nfs_global_settings.yml @@ -0,0 +1,55 @@ +--- +- name: NFS Global Settings Module Operations on PowerScale Storage + hosts: localhost + connection: local + vars: + onefs_host: "10.**.**.**" + port_no: "1234" + api_user: "user" + api_password: "password" + verify_ssl: false + + tasks: + - name: Get NFS global settings + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + + - name: Update service of NFS global settings + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + service: true + nfsv3: + nfsv3_enabled: false + nfsv4: + nfsv40_enabled: true + nfsv41_enabled: true + nfsv42_enabled: false + rpc_minthreads: 17 + rpc_maxthreads: 20 + rquota_enabled: true + + - name: Update service of NFS global settings - Idempotency + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + service: true + nfsv3: + nfsv3_enabled: false + nfsv4: + nfsv40_enabled: true + nfsv41_enabled: true + nfsv42_enabled: false + rpc_minthreads: 17 + rpc_maxthreads: 20 + rquota_enabled: true diff --git a/playbooks/modules/nfs_zone_settings.yml b/playbooks/modules/nfs_zone_settings.yml new file mode 100644 index 00000000..f2d36de0 --- /dev/null +++ b/playbooks/modules/nfs_zone_settings.yml @@ -0,0 +1,52 @@ +--- +- name: NFS zone settings module on PowerScale + hosts: localhost + connection: local + vars: + onefs_host: "xx.xx.xx.xx" + port_no: "000" + api_user: "user" + api_password: "pass" + verify_ssl: false + access_zone: "sample-zone" + + tasks: + - name: Get details of NFS zone settings + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "{{ access_zone }}" + + - name: Update the NFS zone settings - check mode + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "{{ access_zone }}" + nfsv4_allow_numeric_ids: false + nfsv4_domain: "example.com" + nfsv4_no_domain: false + nfsv4_no_domain_uids: false + nfsv4_no_names: false + nfsv4_replace_domain: false + check_mode: true + + - name: Update the NFS zone settings + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "{{ access_zone }}" + nfsv4_allow_numeric_ids: false + nfsv4_domain: "example.com" + nfsv4_no_domain: false + nfsv4_no_domain_uids: false + nfsv4_no_names: false + nfsv4_replace_domain: false diff --git a/plugins/module_utils/storage/dell/shared_library/__init__.py b/plugins/module_utils/storage/dell/shared_library/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/storage/dell/shared_library/auth.py b/plugins/module_utils/storage/dell/shared_library/auth.py new file mode 100644 index 00000000..a073d880 --- /dev/null +++ b/plugins/module_utils/storage/dell/shared_library/auth.py @@ -0,0 +1,89 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +LOG = utils.get_logger('auth') + + +class Auth: + + '''Class with shared auth operations''' + + def __init__(self, auth_api, module): + """ + Initialize the auth class + :param auth_api: The auth sdk instance + :param module: Ansible module object + """ + self.auth_api = auth_api + self.module = module + + def get_group_details(self, name, zone, provider): + """ + Get details of the group + :param name: name of the group + :param zone: zone in which group exists + :param provider: provider type of the group + """ + LOG.info("Getting group details.") + try: + resp = self.auth_api.get_auth_group( + auth_group_id='GROUP:' + name, + zone=zone, provider=provider).to_dict() + return resp + except Exception as e: + error_msg = utils.determine_error(error_obj=e) + error_message = f'Failed to get the group details for group {name} ' \ + f'in zone {zone} and provider {provider} due to' \ + f' error {str(error_msg)}' + LOG.error(error_message) + self.module.fail_json(msg=error_message) + + def get_user_details(self, name, zone, provider): + """ + Get details of the user + :param name: name of the user + :param zone: zone in which user exists + :param provider: provider type of the user + """ + LOG.info("Getting user details.") + try: + resp = self.auth_api.get_auth_user( + auth_user_id='USER:' + name, + zone=zone, provider=provider).to_dict() + return resp + except Exception as e: + error_msg = utils.determine_error(error_obj=e) + error_message = f'Failed to get the user details for {name} in zone ' \ + f'{zone} and provider {provider} due to error ' \ + f'{error_msg}.' + LOG.error(error_message) + self.module.fail_json(msg=error_message) + + def get_group_user_id(self, persona, persona_type, zone): + """ + :param persona: Name and provider type of user or group + :type persona: dict + :param persona_type: Indicates whether user or group + :type persona_type: str + :param zone: Access Zone + :type zone: str + """ + if persona_type == "user": + details = self.get_user_details( + name=persona['name'], + zone=zone, + provider=persona['provider_type'])['users'][0]['uid'] + elif persona_type == "group": + details = self.get_group_details( + name=persona['name'], + zone=zone, + provider=persona['provider_type'])['groups'][0]['gid'] + return details diff --git a/plugins/module_utils/storage/dell/shared_library/powerscale_base.py b/plugins/module_utils/storage/dell/shared_library/powerscale_base.py new file mode 100644 index 00000000..8508aa3c --- /dev/null +++ b/plugins/module_utils/storage/dell/shared_library/powerscale_base.py @@ -0,0 +1,74 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +LOG = utils.get_logger('powerscale_base') + + +class PowerScaleBase: + + '''Powerscale Base Class''' + + def __init__(self, ansible_module, ansible_module_params): + """ + Initialize the powerscale base class + :param ansible_module: Ansible module class + :type ansible_module: AnsibleModule + :param ansible_module_params: Parameters for ansible module class + :type ansible_module_params: dict + """ + self.module_params = utils.get_powerscale_management_host_parameters() + ansible_module_params['argument_spec'].update(self.module_params) + + # Initialize the ansible module + self.module = ansible_module( + **ansible_module_params + ) + + self.result = {"changed": False} + + PREREQS_VALIDATE = utils.validate_module_pre_reqs(self.module.params) + if PREREQS_VALIDATE \ + and not PREREQS_VALIDATE["all_packages_found"]: + self.module.fail_json( + msg=PREREQS_VALIDATE["error_message"]) + + self.api_client = utils.get_powerscale_connection(self.module.params) + self.isi_sdk = utils.get_powerscale_sdk() + LOG.info('Got python SDK instance for provisioning on PowerScale') + LOG.info('Check Mode Flag: %s', self.module.check_mode) + + # Using lazy property for accessing the isi_sdk instances + self._protocol_api = None + self._auth_api = None + + @property + def protocol_api(self): + """ + Returns the protocol API object. + + :return: The protocol API object. + :rtype: isi_sdk.ProtocolsApi + """ + if self._protocol_api is None: + self._protocol_api = self.isi_sdk.ProtocolsApi(self.api_client) + return self._protocol_api + + @property + def auth_api(self): + """ + Returns the auth API object. + + :return: The auth API object. + :rtype: isi_sdk.AuthApi + """ + if self._auth_api is None: + self._auth_api = self.isi_sdk.AuthApi(self.api_client) + return self._auth_api diff --git a/plugins/module_utils/storage/dell/shared_library/protocol.py b/plugins/module_utils/storage/dell/shared_library/protocol.py new file mode 100644 index 00000000..ac5f0bcd --- /dev/null +++ b/plugins/module_utils/storage/dell/shared_library/protocol.py @@ -0,0 +1,49 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +LOG = utils.get_logger('protocol') + + +class Protocol: + + '''Class with shared protocol operations''' + + def __init__(self, protocol_api, module): + """ + Initialize the protocol class + :param protocol_api: The protocol sdk instance + :param module: Ansible module object + """ + self.protocol_api = protocol_api + self.module = module + + def get_nfs_default_settings(self, access_zone): + """ + Get details of an NFS default settings for a given access zone. + :param access_zone: Access zone + :type access_zone: str + :return: NFS default settings + :rtype: dict + """ + msg = f"Getting NFS default settings for {access_zone} access zone" + LOG.info(msg) + try: + nfs_settings_export = self.protocol_api.get_nfs_settings_export(zone=access_zone).to_dict() + if nfs_settings_export: + nfs_default_settings = nfs_settings_export['settings'] + msg = f"NFS default settings are: {nfs_default_settings}" + LOG.info(msg) + return nfs_default_settings + except Exception as e: + error_msg = utils.determine_error(error_obj=e) + error_message = f'Fetching NFS default settings failed with error: {error_msg}' + LOG.error(error_message) + self.module.fail_json(msg=error_message) diff --git a/plugins/module_utils/storage/dell/utils.py b/plugins/module_utils/storage/dell/utils.py index c203764d..5cc23af6 100644 --- a/plugins/module_utils/storage/dell/utils.py +++ b/plugins/module_utils/storage/dell/utils.py @@ -386,6 +386,25 @@ def get_missing_pkgs(): ". Please install the required package(s)." +''' +Convert to seconds from nanoseconds, microseconds, milliseconds +''' + + +def convert_to_seconds(value, units): + if value is not None and value > 0: + if units == 'nanoseconds': + return value / 1000000000 + elif units == 'microseconds': + return value / 1000000 + elif units == 'milliseconds': + return value / 1000 + else: + return value + else: + return 0 + + ''' Returns time in seconds ''' @@ -504,3 +523,21 @@ def get_acl_object(): except ImportError: return None + + +''' +Checks whether parameter has spaces or empty +''' + + +def is_param_empty_spaces(param): + if param is not None and (param.count(" ") > 0 or len(param.strip()) == 0): + return True + + +def get_nfs_map_object(): + try: + import_obj = importlib.import_module(isi_sdk.__name__ + ".models") + return import_obj.NfsExportMapAll() + except ImportError: + return None diff --git a/plugins/modules/info.py b/plugins/modules/info.py index d428e85a..b067b516 100644 --- a/plugins/modules/info.py +++ b/plugins/modules/info.py @@ -28,6 +28,9 @@ specific access zone, network rules, network subnets, network interfaces, node pools, storage pool tiers, smb open files. - Get list of user mapping rules, ldap providers of the PowerScale cluster. +- Get NFS zone settings details of the PowerScale cluster. +- Get NFS default settings details of the PowerScale cluster. +- Get NFS global settings details of the PowerScale cluster. extends_documentation_fragment: - dellemc.powerscale.powerscale @@ -37,6 +40,8 @@ - Spandita Panigrahi(@panigs7) - Pavan Mudunuri(@Pavan-Mudunuri) - Ananthu S Kuttattu(@kuttattz) +- Bhavneet Sharma(@Bhavneet-Sharma) +- Trisha Datta(@trisha-dell) options: include_all_access_zones: @@ -91,6 +96,8 @@ - SMB files - C(smb_files). - User mapping rules - C(user_mapping_rules). - LDAPs - C(ldap). + - NFS zone settings - C(nfs_zone_settings). + - NFS default settings - C(nfs_default_settings). - The list of I(attributes), I(access_zones) and I(nodes) is for the entire PowerScale cluster. - The list of providers for the entire PowerScale cluster. @@ -112,7 +119,8 @@ smb_shares, nfs_exports, nfs_aliases, clients, synciq_reports, synciq_target_reports, synciq_policies, synciq_target_cluster_certificates, synciq_performance_rules, network_groupnets, network_subnets, network_pools, network_rules, network_interfaces, - node_pools, storagepool_tiers, smb_files, user_mapping_rules, ldap] + node_pools, storagepool_tiers, smb_files, user_mapping_rules, ldap, + nfs_zone_settings, nfs_default_settings, nfs_global_settings] type: list elements: str notes: @@ -370,6 +378,33 @@ gather_subset: - ldap scope: "effective" + + - name: Get the NFS zone settings of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + gather_subset: + - nfs_zone_settings + + - name: Get the NFS default settings of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + gather_subset: + - nfs_default_settings + + - name: Get the NFS global settings of the PowerScale cluster + dellemc.powerscale.info: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + gather_subset: + - nfs_global_settings ''' RETURN = r''' @@ -744,6 +779,94 @@ ] } ] +NfsZoneSettings: + description: Details of NFS zone settings. + type: dict + returned: When C(nfs_zone_settings) is in a given I(gather_subset) + contains: + nfsv4_allow_numeric_ids: + description: If C(true), sends owners and groups as UIDs and GIDs + when look up fails or if the I(nfsv4_no_names) + property is set to 1. + type: bool + nfsv4_domain: + description: Specifies the domain through which users and groups + are associated. + type: str + nfsv4_no_domain: + description: If C(true), sends owners and groups without a domain + name. + type: bool + nfsv4_no_domain_uids: + description: If C(true), sends UIDs and GIDs without a domain name. + type: bool + nfsv4_no_names: + description: If C(true), sends owners and groups as UIDs and GIDs. + type: bool + nfsv4_replace_domain: + description: If C(true), replaces the owner or group domain with an + NFS domain name. + type: bool + zone: + description: Specifies the access zone in which the NFS zone + settings apply. + type: str + sample: { + "nfsv4_allow_numeric_ids": true, + "nfsv4_domain": "sample.com", + "nfsv4_no_domain": true, + "nfsv4_no_domain_uids": true, + "nfsv4_no_names": true, + "nfsv4_replace_domain": true, + "zone": "System" + } +NfsGlobalSettings: + description: Details of NFS global settings. + type: dict + returned: When C(nfs_global_settings) is in a given I(gather_subset) + contains: + nfsv3_enabled: + description: Whether NFSv3 protocol is enabled/disabled. + type: bool + nfsv3_rdma_enabled: + description: Whether rdma is enabled for NFSv3 protocol. + type: bool + nfsv40_enabled: + description: Whether version 0 of NFSv4 protocol is enabled/disabled. + type: bool + nfsv41_enabled: + description: Whether version 1 of NFSv4 protocol is enabled/disabled. + type: bool + nfsv42_enabled: + description: Whether version 2 of NFSv4 protocol is enabled/disabled. + type: bool + nfsv4_enabled: + description: Whether NFSv4 protocol is enabled/disabled. + type: bool + rpc_maxthreads: + description: Specifies the maximum number of threads in the nfsd thread pool. + type: int + rpc_minhreads: + description: Specifies the minimum number of threads in the nfsd thread pool. + type: int + rquota_enabled: + description: Whether the rquota protocol is enabled/disabled. + type: bool + service: + description: Whether the NFS service is enabled/disabled. + type: bool + sample: { + "nfsv3_enabled": false, + "nfsv3_rdma_enabled": true, + "nfsv40_enabled": true, + "nfsv41_enabled": true, + "nfsv42_enabled": false, + "nfsv4_enabled": true, + "rpc_maxthreads": 20, + "rpc_minthreads": 17, + "rquota_enabled": true, + "service": true + } NodePools: description: List of the Node pools. type: list @@ -1147,9 +1270,202 @@ } ] ] +nfs_default_settings: + description: The NFS default settings. + type: dict + returned: always + contains: + map_root: + description: Mapping of incoming root users to a specific user and/or group ID. + type: dict + map_non_root: + description: Mapping of non-root users to a specific user and/or group ID. + type: dict + map_failure: + description: Mapping of users to a specific user and/or group ID after a failed auth attempt. + type: dict + name_max_size: + description: Specifies the reported maximum length of a file name. This parameter does + not affect server behavior, but is included to accommodate legacy client + requirements. + type: dict + block_size: + description: Specifies the block size returned by the NFS statfs procedure. + type: dict + directory_transfer_size: + description: Specifies the preferred size for directory read operations. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + read_transfer_max_size: + description: Specifies the maximum buffer size that clients should use on NFS read + requests. This value is used to advise the client of optimal settings for + the server, but is not enforced. + type: dict + read_transfer_multiple: + description: Specifies the preferred multiple size for NFS read requests. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + read_transfer_size: + description: Specifies the preferred size for NFS read requests. This value is used to + advise the client of optimal settings for the server, but is not enforced. + type: dict + write_transfer_max_size: + description: Specifies the maximum buffer size that clients should use on NFS write + requests. This value is used to advise the client of optimal settings for + the server, but is not enforced. + type: dict + write_transfer_multiple: + description: Specifies the preferred multiple size for NFS write requests. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + write_transfer_size: + description: Specifies the preferred multiple size for NFS write requests. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + max_file_size: + description: Specifies the maximum file size for any file accessed from the export. This + parameter does not affect server behavior, but is included to accommodate + legacy client requirements. + type: dict + security_flavors: + description: Specifies the authentication types that are supported for this export. + type: list + commit_asynchronous: + description: True if NFS commit requests execute asynchronously. + type: bool + setattr_asynchronous: + description: True if set attribute operations execute asynchronously. + type: bool + readdirplus: + description: True if 'readdirplus' requests are enabled. Enabling this property might + improve network performance and is only available for NFSv3. + type: bool + return_32bit_file_ids: + description: Limits the size of file identifiers returned by NFSv3+ to 32-bit values (may + require remount). + type: bool + can_set_time: + description: True if the client can set file times through the NFS set attribute + request. This parameter does not affect server behavior, but is included to + accommodate legacy client requirements. + type: bool + map_lookup_uid: + description: True if incoming user IDs (UIDs) are mapped to users in the OneFS user + database. When set to False, incoming UIDs are applied directly to file + operations. + type: bool + symlinks: + description: True if symlinks are supported. This value is used to advise the client of + optimal settings for the server, but is not enforced. + type: bool + write_datasync_action: + description: Specifies the synchronization type for data sync action. + type: str + write_datasync_reply: + description: Specifies the synchronization type for data sync reply. + type: str + write_filesync_action: + description: Specifies the synchronization type for file sync action. + type: str + write_filesync_reply: + description: Specifies the synchronization type for file sync reply. + type: str + write_unstable_action: + description: Specifies the synchronization type for unstable action. + type: str + write_unstable_reply: + description: Specifies the synchronization type for unstable reply. + type: str + encoding: + description: Specifies the default character set encoding of the clients connecting to + the export, unless otherwise specified. + type: str + time_delta: + description: Specifies the resolution of all time values that are returned to the + clients. + type: dict + zone: + description: The zone to which the NFS default settings apply. + type: str + sample: { + 'map_root': { + 'enabled': True, + 'primary_group': { + 'id': None, + 'name': None, + 'type': None + }, + 'secondary_groups': [], + 'user': { + 'id': 'USER:nobody', + 'name': None, + 'type': None + } + }, + 'map_non_root': { + 'enabled': False, + 'primary_group': { + 'id': None, + 'name': None, + 'type': None + }, + 'secondary_groups': [], + 'user': { + 'id': 'USER:nobody', + 'name': None, + 'type': None + } + }, + 'map_failure': { + 'enabled': False, + 'primary_group': { + 'id': None, + 'name': None, + 'type': None + }, + 'secondary_groups': [], + 'user': { + 'id': 'USER:nobody', + 'name': None, + 'type': None + } + }, + 'name_max_size': 255, + 'block_size': 8192, + 'commit_asynchronous': False, + 'directory_transfer_size': 131072, + 'read_transfer_max_size': 1048576, + 'read_transfer_multiple': 512, + 'read_transfer_size': 131072, + 'setattr_asynchronous': False, + 'write_datasync_action': 'DATASYNC', + 'write_datasync_reply': 'DATASYNC', + 'write_filesync_action': 'FILESYNC', + 'write_filesync_reply': 'FILESYNC', + 'write_transfer_max_size': 1048576, + 'write_transfer_multiple': 512, + 'write_transfer_size': 524288, + 'write_unstable_action': 'UNSTABLE', + 'write_unstable_reply': 'UNSTABLE', + 'max_file_size': 9223372036854775807, + 'readdirplus': True, + 'return_32bit_file_ids': False, + 'can_set_time': True, + 'encoding': 'DEFAULT', + 'map_lookup_uid': False, + 'symlinks': True, + 'time_delta': 1e-09, + 'zone': 'sample-zone' + } ''' from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.shared_library.protocol \ + import Protocol from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ import utils @@ -1606,6 +1922,26 @@ def get_network_subnets(self): LOG.error(error_msg) self.module.fail_json(msg=error_msg) + def get_zone_settings(self, access_zone): + """ + Getting the details of NFS zone settings + :param access_zone: Access zone + :return: NFS zone settings + :rtype: dict + """ + try: + zone_settings = (self.protocol_api.get_nfs_settings_zone( + zone=access_zone)).to_dict() + nfs_zone_settings = zone_settings["settings"] + nfs_zone_settings["zone"] = access_zone + return nfs_zone_settings + except Exception as e: + error_msg = (f"Getting zone settings for PowerScale:" + f" {self.module.params['onefs_host']} failed with " + f"error: {utils.determine_error(e)}") + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + def get_node_pools(self): """Getting list of all the node pools of a given PowerScale Storage""" try: @@ -1689,6 +2025,20 @@ def get_user_mapping_rules(self, access_zone): LOG.error(error_msg) self.module.fail_json(msg=error_msg) + def get_nfs_global_settings(self): + """Get the NFS global setings of a given PowerScale Storage""" + try: + nfs_global_settings_details = self.protocol_api.get_nfs_settings_global().to_dict() + msg = f"Got NFS global settings from PowerScale cluster {self.module.params['onefs_host']}" + LOG.info(msg) + return nfs_global_settings_details['settings'] + except Exception as e: + error_msg = ( + f"Getting NFS global settings for PowerScale: {self.module.params['onefs_host']}" + + f" failed with error: {utils.determine_error(e)}") + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + def perform_module_operation(self): """Perform different actions on Gatherfacts based on user parameter chosen in playbook @@ -1725,6 +2075,9 @@ def perform_module_operation(self): smb_files = [] user_mapping_rules = [] ldap = [] + nfs_zone_settings = {} + nfs_default_settings = {} + nfs_global_settings = {} if 'attributes' in str(subset): attributes = self.get_attributes_list() @@ -1776,6 +2129,12 @@ def perform_module_operation(self): user_mapping_rules = self.get_user_mapping_rules(access_zone) if 'ldap' in str(subset): ldap = self.get_ldap_providers(scope) + if 'nfs_zone_settings' in str(subset): + nfs_zone_settings = self.get_zone_settings(access_zone) + if 'nfs_default_settings' in str(subset): + nfs_default_settings = Protocol(self.protocol_api, self.module).get_nfs_default_settings(access_zone) + if 'nfs_global_settings' in str(subset): + nfs_global_settings = self.get_nfs_global_settings() result = dict( Attributes=attributes, @@ -1801,7 +2160,10 @@ def perform_module_operation(self): StoragePoolTiers=storagepool_tiers, SmbOpenFiles=smb_files, UserMappingRules=user_mapping_rules, - LdapProviders=ldap + LdapProviders=ldap, + NfsZoneSettings=nfs_zone_settings, + NfsDefaultSettings=nfs_default_settings, + NfsGlobalSettings=nfs_global_settings ) result.update(SynciqTargetClusterCertificate=synciq_target_cluster_certificates) @@ -1858,7 +2220,10 @@ def get_info_parameters(): 'storagepool_tiers', 'smb_files', 'user_mapping_rules', - 'ldap' + 'ldap', + 'nfs_zone_settings', + 'nfs_default_settings', + 'nfs_global_settings' ]), ) diff --git a/plugins/modules/nfs.py b/plugins/modules/nfs.py index cb463100..0055b0ee 100644 --- a/plugins/modules/nfs.py +++ b/plugins/modules/nfs.py @@ -125,6 +125,76 @@ - Does not present an error condition on unresolvable hosts when creating or modifying an export. type: bool + map_root: + description: + - Specifies the users and groups to which non-root and root clients are mapped. + type: dict + suboptions: + enabled: + description: + - True if the user mapping is applied. + type: bool + default: true + user: + description: + - Specifies the persona name. + type: str + primary_group: + description: + - Specifies the primary group name. + type: str + secondary_groups: + description: + - Specifies the secondary groups. + type: list + elements: dict + suboptions: + name: + description: + - Specifies the group name. + type: str + required: true + state: + description: + - Specifies the group state. + type: str + choices: [absent, present] + default: present + map_non_root: + description: + - Specifies the users and groups to which non-root and root clients are mapped. + type: dict + suboptions: + enabled: + description: + - True if the user mapping is applied. + type: bool + default: true + user: + description: + - Specifies the persona name. + type: str + primary_group: + description: + - Specifies the primary group name. + type: str + secondary_groups: + description: + - Specifies the secondary groups. + type: list + elements: dict + suboptions: + name: + description: + - Specifies the group name. + type: str + required: true + state: + description: + - Specifies the group state. + type: str + choices: [absent, present] + default: present notes: - The I(check_mode) is not supported. @@ -220,6 +290,39 @@ read_only: false state: 'present' + - name: Modify map_root and map_non_root + dellemc.powerscale.nfs: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + path: "" + access_zone: "{{access_zone}}" + map_root: + user: "root" + primary_group: "root" + secondary_groups: + - name: "group_test" + map_non_root: + user: "root" + primary_group: "root" + secondary_groups: + - name: "group_test" + state: "absent" + state: 'present' + + - name: Disable map_root + dellemc.powerscale.nfs: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + path: "" + access_zone: "{{access_zone}}" + map_root: + enabled: false + state: 'present' + - name: Delete NFS Export dellemc.powerscale.nfs: onefs_host: "{{onefs_host}}" @@ -279,6 +382,55 @@ description: description: Description for the export. type: str + map_root: + description: Specifies the users and groups to which non-root and root clients are mapped. + type: complex + contains: + enabled: + description: True if the user mapping is applied. + type: bool + user: + description: Specifies the persona name. + type: complex + contains: + id: + description: Specifies the persona name. + type: str + primary_group: + description: Specifies the primary group. + type: complex + contains: + id: + description: Specifies the primary group name. + type: str + secondary_groups: + description: Specifies the secondary groups. + type: list + map_non_root: + description: Specifies the users and groups to which non-root and root clients are mapped. + type: complex + contains: + enabled: + description: True if the user mapping is applied. + type: bool + user: + description: Specifies the persona details. + type: complex + contains: + id: + description: Specifies the persona name. + type: str + primary_group: + description: Specifies the primary group details. + type: complex + contains: + id: + description: Specifies the primary group name. + type: str + secondary_groups: + description: Specifies the secondary groups details. + type: list + sample: { "all_dir": "false", "block_size": 8192, @@ -286,14 +438,41 @@ "id": 9324, "read_only_client": ["x.x.x.x"], "security_flavors": ["unix", "krb5"], - "zone": "System" + "zone": "System", + "map_root": { + "enabled": true, + "primary_group": { + "id": GROUP:group1, + "name": null, + "type": null + }, + "secondary_groups": [], + "user": { + "id": "USER:user", + "name": null, + "type": null + } + }, + "map_non_root": { + "enabled": false, + "primary_group": { + "id": null, + "name": null, + "type": null + }, + "secondary_groups": [], + "user": { + "id": "USER:nobody", + "name": null, + "type": null + } + } } ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ import utils -import re LOG = utils.get_logger('nfs') @@ -337,7 +516,7 @@ def get_zone_base_path(self, access_zone): get_zones_summary_zone(access_zone)).to_dict() return zone_path["summary"]["path"] except Exception as e: - error_msg = self.determine_error(error_obj=e) + error_msg = utils.determine_error(error_obj=e) error_message = 'Unable to fetch base path of Access Zone {0} ' \ 'failed with error: {1}'.format(access_zone, str(error_msg)) @@ -373,7 +552,7 @@ def get_nfs_export(self, path, access_zone): error_msg = ( "Got error {0} while getting NFS export details for path: " "{1} and access zone: {2}" .format( - self.determine_error(e), + utils.determine_error(e), path, access_zone)) LOG.error(error_msg) @@ -403,7 +582,7 @@ def _get_nfs_export_from_id(self, nfs_export_id, access_zone): error_msg = ( "Got error {0} while getting NFS export details for ID: " "{1} and access zone: {2}" .format( - self.determine_error(e), + utils.determine_error(e), nfs_export_id, access_zone)) LOG.error(error_msg) @@ -427,7 +606,7 @@ def _create_nfs_export_create_params_object(self, path): except Exception as e: error_msg = 'Create NfsExportCreateParams object for path {0}' \ ' failed with error {1}'.format( - path, self.determine_error(e)) + path, utils.determine_error(e)) LOG.error(error_msg) self.module.fail_json(msg=error_msg) @@ -436,6 +615,12 @@ def create_nfs_export(self, path, access_zone, ignore_unresolvable_hosts): Create NFS export for given path and access_zone ''' nfs_export = self._create_nfs_export_create_params_object(path) + nfs_map_root = set_nfs_map(self.module.params.get('map_root'), 'map_root') + if nfs_map_root: + nfs_export.map_root = nfs_map_root + nfs_map_non_root = set_nfs_map(self.module.params.get('map_non_root'), 'map_non_root') + if nfs_map_non_root: + nfs_export.map_non_root = nfs_map_non_root try: msg = ("Creating NFS export with parameters:nfs_export=%s", nfs_export) @@ -451,7 +636,7 @@ def create_nfs_export(self, path, access_zone, ignore_unresolvable_hosts): except Exception as e: error_msg = 'Create NFS export for path: {0} and access zone: {1}' \ ' failed with error: {2}'.format( - path, access_zone, self.determine_error(e)) + path, access_zone, utils.determine_error(e)) LOG.error(error_msg) self.module.fail_json(msg=error_msg) @@ -600,9 +785,16 @@ def modify_nfs_export(self, path, access_zone, ignore_unresolvable_hosts): Modify NFS export in system ''' nfs_export = self.isi_sdk.NfsExport() - client_flag = False + client_flag = map_root_flag = map_non_root_flag = False client_flag, nfs_export = self._check_client_status(nfs_export) - + map_root = set_nfs_map(self.module.params.get('map_root'), 'map_root', self.result.get('NFS_export_details')) + if map_root: + nfs_export.map_root = map_root + map_root_flag = True + map_non_root = set_nfs_map(self.module.params.get('map_non_root'), 'map_non_root', self.result.get('NFS_export_details')) + if map_non_root: + nfs_export.map_non_root = map_non_root + map_non_root_flag = True read_only_flag, read_only_value = self._check_mod_field( 'read_only', 'read_only') all_dirs_flag, all_dirs_value = self._check_mod_field( @@ -614,41 +806,42 @@ def modify_nfs_export(self, path, access_zone, ignore_unresolvable_hosts): if all( field_mod_flag is False for field_mod_flag in [ - client_flag, read_only_flag, all_dirs_flag, description_flag, - security_flag]) and self.module.params['ignore_unresolvable_hosts'] is not True: + client_flag, read_only_flag, all_dirs_flag, description_flag, map_root_flag, + map_non_root_flag, security_flag]) and self.module.params['ignore_unresolvable_hosts'] is not True: LOG.info( 'No change detected for the NFS Export, returning changed = False') return False else: - nfs_export.read_only = read_only_value if read_only_flag else None nfs_export.all_dirs = all_dirs_value if all_dirs_flag else None nfs_export.description = description_value if description_flag else None LOG.debug('Modifying NFS Export with %s details', nfs_export) + return self.perform_modify_nfs_export(nfs_export, path, access_zone, ignore_unresolvable_hosts) - try: - if ignore_unresolvable_hosts is not True: - self.protocol_api.update_nfs_export( - nfs_export, - self.result['NFS_export_details']['id'], - zone=self.result['NFS_export_details']['zone']) - else: - self.protocol_api.update_nfs_export( - nfs_export, - self.result['NFS_export_details']['id'], - zone=self.result['NFS_export_details']['zone'], - ignore_unresolvable_hosts=ignore_unresolvable_hosts) - # update result with updated details - self.result['NFS_export_details'] = self.get_nfs_export( - path, access_zone) - return True + def perform_modify_nfs_export(self, nfs_export, path, access_zone, ignore_unresolvable_hosts): + try: + if ignore_unresolvable_hosts is not True: + self.protocol_api.update_nfs_export( + nfs_export, + self.result['NFS_export_details']['id'], + zone=self.result['NFS_export_details']['zone']) + else: + self.protocol_api.update_nfs_export( + nfs_export, + self.result['NFS_export_details']['id'], + zone=self.result['NFS_export_details']['zone'], + ignore_unresolvable_hosts=ignore_unresolvable_hosts) + # update result with updated details + self.result['NFS_export_details'] = self.get_nfs_export( + path, access_zone) + return True - except Exception as e: - error_msg = 'Modify NFS export for path: {0} and access zone:' \ - ' {1} failed with error: {2}'.format( - path, access_zone, self.determine_error(e)) - LOG.error(error_msg) - self.module.fail_json(msg=error_msg) + except Exception as e: + error_msg = 'Modify NFS export for path: {0} and access zone:' \ + ' {1} failed with error: {2}'.format( + path, access_zone, utils.determine_error(e)) + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) def delete_nfs_export(self): ''' @@ -671,18 +864,10 @@ def delete_nfs_export(self): nfs_export['paths'][0], nfs_export['zone'], nfs_export['id'], - self.determine_error(e))) + utils.determine_error(e))) LOG.error(error_msg) self.module.fail_json(msg=error_msg) - def determine_error(self, error_obj): - '''Format the error object''' - if isinstance(error_obj, utils.ApiException): - error = re.sub("[\n \"]+", ' ', str(error_obj.body)) - else: - error = str(error_obj) - return error - def effective_path(self, access_zone, path): """Get the effective path for any access zone""" if access_zone is not None and access_zone.lower() == "system": @@ -761,6 +946,22 @@ def get_nfs_parameters(self): type='list', elements='str', choices=['unix', 'kerberos', 'kerberos_integrity', 'kerberos_privacy']), + map_root=dict(type='dict', options=dict( + enabled=dict(type='bool', default=True), + primary_group=dict(), + secondary_groups=dict(type='list', elements='dict', options=dict( + name=dict(required=True), + state=dict(choices=['present', 'absent'], default='present'))), + user=dict()) + ), + map_non_root=dict(type='dict', options=dict( + enabled=dict(type='bool', default=True), + primary_group=dict(), + secondary_groups=dict(type='list', elements='dict', options=dict( + name=dict(required=True), + state=dict(choices=['present', 'absent'], default='present'))), + user=dict()) + ), state=dict(required=True, type='str', choices=['present', 'absent']) ) @@ -782,6 +983,120 @@ def get_security_keys(security_flavors): return None +def set_nfs_map(map_params, map_type, nfs_export_details=None): + """ + Set the map root or non-root object based on input + + Args: + map_params (dict): The map parameters + map_type (str): The type of map (root or non-root) + nfs_export_details (dict, optional): The NFS export details + + Returns: + nfs_map_object (object): The modified NFS map object + """ + if map_params is not None: + nfs_map_object = utils.get_nfs_map_object() + nfs_map_object = initialize_nfs_map(nfs_map_object, nfs_export_details, map_type) + nfs_map_object.enabled = map_params.get('enabled') + if nfs_map_object.enabled is not False: + nfs_map_object = set_nfs_map_object(nfs_map_object, map_params) + + changed = is_nfs_map_modified(map_type, nfs_map_object, nfs_export_details, map_params) + if changed or not nfs_export_details: + return nfs_map_object + + +def set_nfs_map_object(nfs_map_object, map_params): + if map_params['user']: + user = {'name': map_params.get('user')} + nfs_map_object.user = user + + if map_params['primary_group']: + group = {'name': map_params.get('primary_group')} + nfs_map_object.primary_group = group + + groups = nfs_map_object.secondary_groups.copy() + new_groups = [] + secondary_groups = map_params.get('secondary_groups') or [] + for secondary_group in secondary_groups: + group = [nfs_map for nfs_map in groups if nfs_map.get('id').split(':')[1] == secondary_group.get('name')] + if secondary_group.get('state') == 'absent' and group: + groups.remove(group[0]) + elif secondary_group.get('state') != 'absent' and not group: + group = {'id': secondary_group.get('name')} + new_groups.append(group) + nfs_map_object.secondary_groups = groups + new_groups + return nfs_map_object + + +def initialize_nfs_map(nfs_map_object, nfs_export_details, map_type): + """ + Initialize the map object with details from nfs_export_details. + + Args: + nfs_map_object (object): The map object to be initialized. + nfs_export_details (dict): The details of the NFS export. + map_type (str): The type of map to be initialized. + + Returns: + object: The initialized map object. + + """ + if nfs_export_details and nfs_export_details[map_type]: + nfs_map_object.enabled = {'id': nfs_export_details[map_type].get('enabled')} + nfs_map_object.user = {'id': nfs_export_details[map_type].get('user').get('id')} \ + if nfs_export_details[map_type].get('user').get('id') else None + nfs_map_object.primary_group = {'id': nfs_export_details[map_type].get('primary_group').get('id')} \ + if nfs_export_details[map_type].get('primary_group').get('id') else None + nfs_map_object.secondary_groups = nfs_export_details[map_type]['secondary_groups'] + else: + nfs_map_object.user = nfs_map_object.primary_group = None + nfs_map_object.secondary_groups = [] + return nfs_map_object + + +def is_nfs_map_modified(map_type, nfs_export_map, nfs_export_details, nfs_map_params): + """ + Check if nfs map object is modified. + + Args: + map_type (str): Type of the map. + nfs_export_map (NfsExportMap): The nfs export map object. + nfs_export_details (dict): A dictionary containing details of nfs export maps. + nfs_map_params (dict): A dictionary containing the parameters to compare with the export map object. + + Returns: + bool: True if the nfs map object is modified, False otherwise. + """ + if nfs_export_map is not None and nfs_export_details: + if nfs_map_params['enabled'] is not None and nfs_export_map.enabled != nfs_export_details[map_type]['enabled']: + return True + if nfs_map_params['enabled'] is not False: + if is_map_user_modified(nfs_map_params, nfs_export_details, nfs_export_map, map_type) or \ + is_map_primary_group_modified(nfs_map_params, nfs_export_details, nfs_export_map, map_type) or \ + is_map_secondary_groups_modified(nfs_map_params, nfs_export_details, nfs_export_map, map_type): + return True + + +def is_map_user_modified(nfs_map_params, nfs_export_details, nfs_export_map, map_type): + if nfs_map_params['user'] is not None and \ + nfs_export_map.user.get('name') != nfs_export_details[map_type]['user']['id'].split(':')[1]: + return True + + +def is_map_primary_group_modified(nfs_map_params, nfs_export_details, nfs_export_map, map_type): + if nfs_map_params['primary_group'] is not None and \ + nfs_export_map.primary_group.get('name') != nfs_export_details[map_type]['primary_group']['id'].split(':')[1]: + return True + + +def is_map_secondary_groups_modified(nfs_map_params, nfs_export_details, nfs_export_map, map_type): + if nfs_map_params['secondary_groups'] is not None and \ + nfs_export_map.secondary_groups != nfs_export_details[map_type]['secondary_groups']: + return True + + def main(): ''' Create PowerScale_NFS export object and perform action on it based on user input from playbook''' diff --git a/plugins/modules/nfs_default_settings.py b/plugins/modules/nfs_default_settings.py new file mode 100644 index 00000000..23e279c1 --- /dev/null +++ b/plugins/modules/nfs_default_settings.py @@ -0,0 +1,1133 @@ +#!/usr/bin/python +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Ansible module for managing NFS Default Settings on PowerScale""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: nfs_default_settings +version_added: '2.2.0' +short_description: Manage NFS default settings on a PowerScale Storage System +description: +- Managing NFS default settings on an PowerScale system includes getting details of an NFS default settings + and modifying different attributes of the NFS default settings. + +extends_documentation_fragment: + - dellemc.powerscale.powerscale + +author: +- Ananthu S Kuttattu(@kuttattz) + +options: + map_root: + description: + - User and group mapping. + - Map incoming root users to a specific user and/or group ID. + required: False + type: dict + suboptions: + enabled: + description: + - Indicates if user mapping is enabled or not. + - True if the user mapping is applied. + type: bool + default: True + primary_group: + description: + - Specifies name of the primary group. + required: False + type: str + secondary_groups: + description: + - Specifies name and state of the secondary groups. + type: list + elements: dict + required: False + suboptions: + name: + description: + - Name of the group. + type: str + required: True + state: + description: + - State of the secondary group. + type: str + choices: ['present', 'absent'] + default: 'present' + user: + description: + - Specifies name of the user. + required: False + type: str + map_non_root: + description: + - User and group mapping. + - Map non-root users to a specific user and/or group ID. + required: False + type: dict + suboptions: + enabled: + description: + - Indicates if user mapping is enabled or not. + - True if the user mapping is applied. + type: bool + default: True + primary_group: + description: + - Specifies name of the primary group. + required: False + type: str + secondary_groups: + description: + - Specifies name and state of the secondary groups. + type: list + elements: dict + required: False + suboptions: + name: + description: + - Name of the group. + type: str + required: True + state: + description: + - State of the secondary group. + type: str + choices: ['present', 'absent'] + default: 'present' + user: + description: + - Specifies name of the user. + required: False + type: str + map_failure: + description: + - User and group mapping. + - Map users to a specific user and/or group ID after a failed auth attempt. + required: False + type: dict + suboptions: + enabled: + description: + - Indicates if user mapping is enabled or not. + - True if the user mapping is applied. + type: bool + default: True + primary_group: + description: + - Specifies name of the primary group. + required: False + type: str + secondary_groups: + description: + - Specifies name and state of the secondary groups. + type: list + elements: dict + required: False + suboptions: + name: + description: + - Name of the group. + type: str + required: True + state: + description: + - State of the secondary group. + type: str + choices: ['present', 'absent'] + default: 'present' + user: + description: + - Specifies name of the user. + required: False + type: str + file_name_max_size: + description: + - Specifies the reported maximum length of a file name. + - This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + block_size: + description: + - Specifies the block size returned by the NFS statfs procedure. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + directory_transfer_size: + description: + - Specifies the preferred size for directory read operations. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + read_transfer_max_size: + description: + - Specifies the maximum buffer size that clients should use on NFS read requests. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + read_transfer_multiple: + description: + - Specifies the preferred multiple size for NFS read requests. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + read_transfer_size: + description: + - Specifies the preferred size for NFS read requests. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + write_transfer_max_size: + description: + - Specifies the maximum buffer size that clients should use on NFS write requests. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + write_transfer_multiple: + description: + - Specifies the preferred multiple size for NFS write requests. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + write_transfer_size: + description: + - Specifies the preferred multiple size for NFS write requests. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + max_file_size: + description: + - Specifies the maximum file size for any file accessed from the export. + - This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + type: dict + required: False + suboptions: + size_value: + description: + - Size value. + type: int + required: true + size_unit: + description: + - Unit for the size value. + type: str + required: true + choices: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + security_flavors: + description: + - Specifies the authentication types that are supported for this export. + type: list + required: False + elements: str + choices: ['unix', 'kerberos', 'kerberos_integrity', 'kerberos_privacy'] + commit_asynchronous: + description: + - True if NFS commit requests execute asynchronously. + type: bool + required: False + setattr_asynchronous: + description: + - True if set attribute operations execute asynchronously. + type: bool + required: False + readdirplus: + description: + - True if 'readdirplus' requests are enabled. + - Enabling this property might improve network performance and is only available for NFSv3. + type: bool + required: False + return_32bit_file_ids: + description: + - Limits the size of file identifiers returned by NFSv3+ to 32-bit values (may require remount). + type: bool + required: False + can_set_time: + description: + - True if the client can set file times through the NFS set attribute request. + - This parameter does not affect server behavior, but is included to accommodate legacy client requirements. + type: bool + required: False + map_lookup_uid: + description: + - True if incoming user IDs (UIDs) are mapped to users in the OneFS user database. + - When set to False, incoming UIDs are applied directly to file operations. + type: bool + required: False + symlinks: + description: + - True if symlinks are supported. + - This value is used to advise the client of optimal settings for the server, but is not enforced. + type: bool + required: False + write_datasync_action: + description: + - Specifies the synchronization type for datasync action. + type: str + choices: ['DATASYNC', 'FILESYNC', 'UNSTABLE'] + write_datasync_reply: + description: + - Specifies the synchronization type for datasync reply. + type: str + choices: ['DATASYNC', 'FILESYNC', 'UNSTABLE'] + write_filesync_action: + description: + - Specifies the synchronization type for filesync action. + type: str + choices: ['DATASYNC', 'FILESYNC', 'UNSTABLE'] + write_filesync_reply: + description: + - Specifies the synchronization type for filesync reply. + type: str + choices: ['DATASYNC', 'FILESYNC', 'UNSTABLE'] + write_unstable_action: + description: + - Specifies the synchronization type for unstable action. + type: str + choices: ['DATASYNC', 'FILESYNC', 'UNSTABLE'] + write_unstable_reply: + description: + - Specifies the synchronization type for unstable reply. + type: str + choices: ['DATASYNC', 'FILESYNC', 'UNSTABLE'] + encoding: + description: + - Specifies the default character set encoding of the clients connecting to the export, unless otherwise specified. + type: str + time_delta: + description: + - Specifies the resolution of all time values that are returned to the clients. + type: dict + required: False + suboptions: + time_value: + description: + - Time value. + type: float + required: true + time_unit: + description: + - Unit for the time value. + type: str + required: true + choices: ['seconds', 'nanoseconds', 'milliseconds', 'microseconds'] + access_zone: + description: + - The zone to which the NFS default settings apply. + default: 'System' + type: str + +notes: +- The I(check_mode) is supported. + +''' + +EXAMPLES = r''' + - name: Get NFS default settings + dellemc.powerscale.nfs_default_settings: + onefs_host: "{{onefs_host}}" + verify_ssl: "{{verify_ssl}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + access_zone: "sample-zone" + + - name: Update the NFS default settings + dellemc.powerscale.nfs_default_settings: + onefs_host: "{{ onefs_host }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + access_zone: "sample-zone" + block_size: + size_value: 5 + size_unit: 'KB' + commit_asynchronous: False + encoding: 'UTF8' + map_root: + enabled: true + primary_group: 'test_group_1' + secondary_groups: + - name: 'test_group_2' + - name: 'test_group_3' + state: 'absent' + user: 'test_user' + map_non_root: + enabled: true + primary_group: 'test_non_root_group' + secondary_groups: + - name: 'test_non_root_group_2' + - name: 'test_non_root_group_3' + state: 'absent' + user: 'test_non_root_user' + readdirplus: true + time_delta: + time_value: 5 + time_unit: 'seconds' + write_filesync_action: 'DATASYNC' + security_flavors: + - unix + - kerberos +''' + +RETURN = r''' +changed: + description: A boolean indicating if the task had to make changes. + returned: always + type: bool + sample: "false" +nfs_default_settings: + description: The NFS default settings. + type: dict + returned: always + contains: + map_root: + description: Mapping of incoming root users to a specific user and/or group ID. + type: dict + map_non_root: + description: Mapping of non-root users to a specific user and/or group ID. + type: dict + map_failure: + description: Mapping of users to a specific user and/or group ID after a failed auth attempt. + type: dict + name_max_size: + description: Specifies the reported maximum length of a file name. This parameter does + not affect server behavior, but is included to accommodate legacy client + requirements. + type: dict + block_size: + description: Specifies the block size returned by the NFS statfs procedure. + type: dict + directory_transfer_size: + description: Specifies the preferred size for directory read operations. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + read_transfer_max_size: + description: Specifies the maximum buffer size that clients should use on NFS read + requests. This value is used to advise the client of optimal settings for + the server, but is not enforced. + type: dict + read_transfer_multiple: + description: Specifies the preferred multiple size for NFS read requests. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + read_transfer_size: + description: Specifies the preferred size for NFS read requests. This value is used to + advise the client of optimal settings for the server, but is not enforced. + type: dict + write_transfer_max_size: + description: Specifies the maximum buffer size that clients should use on NFS write + requests. This value is used to advise the client of optimal settings for + the server, but is not enforced. + type: dict + write_transfer_multiple: + description: Specifies the preferred multiple size for NFS write requests. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + write_transfer_size: + description: Specifies the preferred multiple size for NFS write requests. This value is + used to advise the client of optimal settings for the server, but is not + enforced. + type: dict + max_file_size: + description: Specifies the maximum file size for any file accessed from the export. This + parameter does not affect server behavior, but is included to accommodate + legacy client requirements. + type: dict + security_flavors: + description: Specifies the authentication types that are supported for this export. + type: list + commit_asynchronous: + description: True if NFS commit requests execute asynchronously. + type: bool + setattr_asynchronous: + description: True if set attribute operations execute asynchronously. + type: bool + readdirplus: + description: True if 'readdirplus' requests are enabled. Enabling this property might + improve network performance and is only available for NFSv3. + type: bool + return_32bit_file_ids: + description: Limits the size of file identifiers returned by NFSv3+ to 32-bit values (may + require remount). + type: bool + can_set_time: + description: True if the client can set file times through the NFS set attribute + request. This parameter does not affect server behavior, but is included to + accommodate legacy client requirements. + type: bool + map_lookup_uid: + description: True if incoming user IDs (UIDs) are mapped to users in the OneFS user + database. When set to False, incoming UIDs are applied directly to file + operations. + type: bool + symlinks: + description: True if symlinks are supported. This value is used to advise the client of + optimal settings for the server, but is not enforced. + type: bool + write_datasync_action: + description: Specifies the synchronization type for data sync action. + type: str + write_datasync_reply: + description: Specifies the synchronization type for data sync reply. + type: str + write_filesync_action: + description: Specifies the synchronization type for file sync action. + type: str + write_filesync_reply: + description: Specifies the synchronization type for file sync reply. + type: str + write_unstable_action: + description: Specifies the synchronization type for unstable action. + type: str + write_unstable_reply: + description: Specifies the synchronization type for unstable reply. + type: str + encoding: + description: Specifies the default character set encoding of the clients connecting to + the export, unless otherwise specified. + type: str + time_delta: + description: Specifies the resolution of all time values that are returned to the + clients. + type: dict + zone: + description: The zone to which the NFS default settings apply. + type: str + sample: { + 'map_root': { + 'enabled': True, + 'primary_group': { + 'id': None, + 'name': None, + 'type': None + }, + 'secondary_groups': [], + 'user': { + 'id': 'USER:nobody', + 'name': None, + 'type': None + } + }, + 'map_non_root': { + 'enabled': False, + 'primary_group': { + 'id': None, + 'name': None, + 'type': None + }, + 'secondary_groups': [], + 'user': { + 'id': 'USER:nobody', + 'name': None, + 'type': None + } + }, + 'map_failure': { + 'enabled': False, + 'primary_group': { + 'id': None, + 'name': None, + 'type': None + }, + 'secondary_groups': [], + 'user': { + 'id': 'USER:nobody', + 'name': None, + 'type': None + } + }, + 'name_max_size': 255, + 'block_size': 8192, + 'commit_asynchronous': False, + 'directory_transfer_size': 131072, + 'read_transfer_max_size': 1048576, + 'read_transfer_multiple': 512, + 'read_transfer_size': 131072, + 'setattr_asynchronous': False, + 'write_datasync_action': 'DATASYNC', + 'write_datasync_reply': 'DATASYNC', + 'write_filesync_action': 'FILESYNC', + 'write_filesync_reply': 'FILESYNC', + 'write_transfer_max_size': 1048576, + 'write_transfer_multiple': 512, + 'write_transfer_size': 524288, + 'write_unstable_action': 'UNSTABLE', + 'write_unstable_reply': 'UNSTABLE', + 'max_file_size': 9223372036854775807, + 'readdirplus': True, + 'return_32bit_file_ids': False, + 'can_set_time': True, + 'encoding': 'DEFAULT', + 'map_lookup_uid': False, + 'symlinks': True, + 'time_delta': 1e-09, + 'zone': 'sample-zone' + } +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.shared_library.powerscale_base \ + import PowerScaleBase +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.shared_library.protocol \ + import Protocol +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +LOG = utils.get_logger('nfs_default_settings') + + +class NFSDefaultSettings(PowerScaleBase): + + '''Class with NFS default settings operations''' + + def __init__(self): + ''' Define all parameters required by this module''' + + ansible_module_params = { + 'argument_spec': self.get_nfs_default_settings_parameters(), + 'supports_check_mode': True + } + + super().__init__(AnsibleModule, ansible_module_params) + + self.result["nfs_default_settings"] = {} + + def get_nfs_default_settings(self, access_zone): + """ + Get details of an NFS default settings for a given access zone. + :param access_zone: Access zone + :type access_zone: str + :return: NFS default settings + :rtype: dict + """ + return Protocol(self.protocol_api, self.module).get_nfs_default_settings(access_zone) + + def form_list_of_secondary_groups(self, existing_groups, secondary_groups, access_zone): + """ + Form list of secondary groups + :param existing_group: Existing groups + :type existing_group: list + :param secondary_groups: Secondary groups + :type secondary_groups: list + :param access_zone: Access zone + :type access_zone: str + """ + secondary_groups_list = [] + existing_groups_list = [existing_group['id'] for existing_group in existing_groups] + old_groups_list = existing_groups + for group in secondary_groups: + state = group['state'] + group = { + 'id': 'GROUP:' + group['name'], + 'type': 'group'} + if state == 'present' and group['id'] not in existing_groups_list: + secondary_groups_list.append({'id': group['id']}) + elif state == 'absent' and group['id'] in existing_groups_list: + existing_groups = [ex_grp for ex_grp in existing_groups if ex_grp['id'] != group['id']] + secondary_groups_list.extend(existing_groups) + existing_groups_id_list = [existing_group['id'] for existing_group in old_groups_list] + secondary_groups_id_list = [secondary_group['id'] for secondary_group in secondary_groups_list] + if set(existing_groups_id_list) != set(secondary_groups_id_list): + return secondary_groups_list + + def form_primary_group_and_user_dict(self, persona_type, existing_map_params, name): + """ + Form primary group or user dict + :param persona_type: Indicating group or user + :type persona_type: str + :param existing_map_params: Existing map params + :type existing_map_params: dict + :param map_params: Map params + :type map_params: dict + :return: Primary group or user dict + """ + persona_item = { + 'id': persona_type + ':' + name, + 'type': persona_type.lower()} + if existing_map_params['id'] != persona_item['id']: + return persona_item + + def fill_map_dict(self, map_dict, key, nfs_default_settings): + """ + Fill map dict for update + :param map_dict: Map dict + :type map_dict: dict + :param nfs_default_settings: NFS default settings + :type nfs_default_settings: dict + :param key: Key + :type key: str + """ + sub_key_list = ['enabled', 'primary_group', 'secondary_groups', 'user'] + for sub_key in sub_key_list: + if sub_key not in map_dict[key]: + map_dict[key][sub_key] = nfs_default_settings[key][sub_key] + return map_dict + + def check_and_delete_map_key(self, map_dict, key): + """ + Check and delete map dict key + :param map_dict: Map dict + :type map_dict: dict + :param key: Map key + :type key: str + """ + if map_dict[key] is None: + del map_dict[key] + + def check_modify_for_map_key(self, map_dict, key, nfs_default_settings, module_params): + """ + Check and delete map dict key + :param map_dict: Map dict + :type map_dict: dict + :param nfs_default_settings: NFS default settings + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :param key: Key + :type key: str + """ + if module_params[key] and 'enabled' in module_params[key] and \ + nfs_default_settings[key]['enabled'] != module_params[key]['enabled']: + if module_params[key]['enabled'] is False: + map_dict[key] = { + 'enabled': False + } + return map_dict + else: + map_dict[key]['enabled'] = module_params[key]['enabled'] + + check_keys = ['primary_group', 'secondary_groups', 'user'] + persona_map = { + 'primary_group': 'GROUP', + 'user': 'USER' + } + for check_key in check_keys: + if module_params[key] and check_key in module_params[key] and module_params[key][check_key]: + if check_key == 'secondary_groups': + map_dict[key][check_key] = self.form_list_of_secondary_groups(nfs_default_settings[key][check_key], + module_params[key][check_key], + module_params['access_zone']) + self.check_and_delete_map_key(map_dict[key], check_key) + else: + map_dict[key][check_key] = self.form_primary_group_and_user_dict(persona_map[check_key], + nfs_default_settings[key][check_key], + module_params[key][check_key]) + self.check_and_delete_map_key(map_dict[key], check_key) + return map_dict + + def form_map_dict(self, nfs_default_settings, module_params): + """ + Form the modify dict. + :param nfs_default_settings: NFS default settings details + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :return: map_dict + :rtype: dict + """ + LOG.info('Form modification dict for map settings') + map_dict = {} + map_key_list = ['map_root', 'map_non_root', 'map_failure'] + for key in map_key_list: + if module_params[key] and 'enabled' in module_params[key] \ + and module_params[key]['enabled'] is False and module_params[key]['enabled'] == nfs_default_settings[key]['enabled']: + continue + + map_dict[key] = {} + map_dict = self.check_modify_for_map_key(map_dict, key, nfs_default_settings, module_params) + + if not map_dict[key]: + del map_dict[key] + elif 'enabled' not in map_dict[key] or ('enabled' in map_dict[key] and map_dict[key]['enabled']): + map_dict = self.fill_map_dict(map_dict, key, nfs_default_settings) + + msg = f'Forming modification dict for map settings completed: {map_dict}' + LOG.info(msg) + return map_dict + + def form_size_dict(self, nfs_default_settings, module_params): + """ + Form the modify dict. + :param nfs_default_settings: NFS default settings details + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :return: size_dict + :rtype: dict + """ + LOG.info('Form modification dict for size settings') + size_dict = {} + size_params = ['block_size', 'directory_transfer_size', 'read_transfer_max_size', 'read_transfer_multiple', + 'read_transfer_size', 'write_transfer_max_size', 'write_transfer_multiple', 'write_transfer_size', + 'max_file_size'] + + for key in size_params: + if key in module_params and module_params[key] is not None and \ + utils.get_size_bytes(module_params[key]['size_value'], module_params[key]['size_unit']) != nfs_default_settings[key]: + size_dict[key] = utils.get_size_bytes(module_params[key]['size_value'], module_params[key]['size_unit']) + + if 'file_name_max_size' in module_params and module_params['file_name_max_size'] and \ + utils.get_size_bytes(module_params['file_name_max_size']['size_value'], + module_params['file_name_max_size']['size_unit']) != nfs_default_settings['name_max_size']: + size_dict['name_max_size'] = utils.get_size_bytes(module_params['file_name_max_size']['size_value'], + module_params['file_name_max_size']['size_unit']) + msg = f'Forming modification dict for size settings completed: {size_dict}' + LOG.info(msg) + return size_dict + + def form_security_dict(self, nfs_default_settings, module_params): + """ + Form the modify dict. + :param nfs_default_settings: NFS default settings details + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :return: security_dict + :rtype: dict + """ + LOG.info('Form modification dict for security flavors') + security_dict = {} + security_flavors_mapping = { + "unix": "unix", + "kerberos": "krb5", + "kerberos_integrity": "krb5i", + "kerberos_privacy": "krb5p" + } + security_flavors = module_params['security_flavors'] + if security_flavors: + security_flavors = [security_flavors_mapping[flavor] for flavor in security_flavors] + if security_flavors != nfs_default_settings['security_flavors']: + security_dict['security_flavors'] = security_flavors + msg = f'Forming modification dict for security flavors completed: {security_dict}' + LOG.info(msg) + return security_dict + + def form_sync_bool_dict(self, nfs_default_settings, module_params): + """ + Form the modify dict. + :param nfs_default_settings: NFS default settings details + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :return: sync_bool_dict + :rtype: dict + """ + LOG.info('Form modification dict for bool and sync settings') + sync_bool_dict = {} + sync_bool_params = ['commit_asynchronous', 'setattr_asynchronous', 'readdirplus', + 'return_32bit_file_ids', 'can_set_time', 'map_lookup_uid', + 'symlinks', 'write_unstable_action', 'write_unstable_reply', + 'write_filesync_action', 'write_filesync_reply', + 'write_datasync_action', 'write_datasync_reply', 'encoding'] + for key in sync_bool_params: + if key in module_params and module_params[key] is not None and\ + nfs_default_settings[key] != module_params[key]: + sync_bool_dict[key] = module_params[key] + msg = f'Forming modification dict for bool and sync settings completed: {sync_bool_dict}' + LOG.info(msg) + return sync_bool_dict + + def form_time_dict(self, nfs_default_settings, module_params): + """ + Form the modify dict. + :param nfs_default_settings: NFS default settings details + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :return: time_dict + :rtype: dict + """ + LOG.info('Form modification dict for time delta') + time_dict = {} + if 'time_delta' in module_params and module_params['time_delta'] and nfs_default_settings['time_delta'] \ + != utils.convert_to_seconds(module_params['time_delta']['time_value'], module_params['time_delta']['time_unit']): + time_dict['time_delta'] = utils.convert_to_seconds(module_params['time_delta']['time_value'], module_params['time_delta']['time_unit']) + msg = f'Forming modification dict for time delta completed: {time_dict}' + LOG.info(msg) + return time_dict + + def form_modify_dict(self, nfs_default_settings, module_params): + """ + Form the modify dict. + :param nfs_default_settings: NFS default settings details + :type nfs_default_settings: dict + :param module_params: Module parameters + :type module_params: dict + :return: modify_dict + :rtype: dict + """ + msg = f'Form modification dict {module_params}' + LOG.info(msg) + modify_dict = {} + try: + modify_dict.update(self.form_map_dict(nfs_default_settings, module_params)) + modify_dict.update(self.form_size_dict(nfs_default_settings, module_params)) + modify_dict.update(self.form_security_dict(nfs_default_settings, module_params)) + modify_dict.update(self.form_sync_bool_dict(nfs_default_settings, module_params)) + modify_dict.update(self.form_time_dict(nfs_default_settings, module_params)) + except Exception as exp: + msg = f'Forming modification dict failed with error: {exp}' + LOG.info(msg) + self.module.fail_json(msg=msg) + msg = f'Forming modification dict completed: {modify_dict}' + LOG.info(msg) + return modify_dict + + def form_nfs_default_settings_object(self, modify_dict, access_zone): + """ + Form nfs default settings object + :param modify_dict: NFS default settings module params + :return: NFS default settings object. + """ + LOG.info('Forming nfs default settings object') + map_key_list = ['map_root', 'map_non_root', 'map_failure'] + for key in map_key_list: + if key in modify_dict and modify_dict[key]: + if modify_dict[key]['enabled'] is False: + modify_dict[key] = self.isi_sdk.NfsSettingsExportSettingsMapAll(**modify_dict[key]) + continue + modify_dict[key]['user'] = self.isi_sdk.AuthAccessAccessItemFileGroup(**modify_dict[key]['user']) + modify_dict[key]['primary_group'] = self.isi_sdk.AuthAccessAccessItemFileGroup(**modify_dict[key]['primary_group']) + secondary_groups = [] + for sec_gp in modify_dict[key]['secondary_groups']: + secondary_groups.append(self.isi_sdk.NfsExportMapAllSecondaryGroups(**sec_gp)) + modify_dict[key]['secondary_groups'] = secondary_groups + modify_dict[key]['enabled'] = True + modify_dict[key] = self.isi_sdk.NfsSettingsExportSettingsMapAll(**modify_dict[key]) + + modify_dict['zone'] = access_zone + nfs_default_settings_object = self.isi_sdk.NfsSettingsExportSettings(**modify_dict) + LOG.info('Forming nfs default settings object completed') + return nfs_default_settings_object + + def modify_nfs_default_settings(self, module_params, access_zone): + """ + Modify the NFS default settings + :param module_params: Module params + :type module_params: dict + :param access_zone: Access zone + :type access_zone: str + :return: True if successful + :rtype: bool + """ + try: + modify_params_obj = self.form_nfs_default_settings_object(module_params, access_zone) + msg = f'Modifying NFS default settings for access zone: {access_zone}' + LOG.info(msg) + if not self.module.check_mode: + self.protocol_api.update_nfs_settings_export( + modify_params_obj, zone=access_zone) + LOG.info("Successfully modified the NFS default settings") + return True + except Exception as e: + error = utils.determine_error(e) + error_msg = f'Modifying NFS default settings for access zone: {access_zone} failed with error: {error}' + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def get_size_paramters(self): + return dict(type='dict', options=dict( + size_value=dict(type='int', required=True), + size_unit=dict(type='str', required=True, choices=['B', 'KB', 'MB', 'GB', 'TB', 'PB']))) + + def get_nfs_map_parameters(self): + return dict(type='dict', options=dict( + enabled=dict(type='bool', default=True), + primary_group=dict(type='str', required=False), + user=dict(type='str', required=False), + secondary_groups=dict(type='list', elements='dict', options=dict( + name=dict(type='str', required=True), + state=dict(type='str', choices=['present', 'absent'], default='present'))))) + + def get_sync_parameters(self): + return dict(type='str', choices=['DATASYNC', 'FILESYNC', 'UNSTABLE']) + + def get_nfs_default_settings_parameters(self): + return dict( + map_root=self.get_nfs_map_parameters(), + map_non_root=self.get_nfs_map_parameters(), + map_failure=self.get_nfs_map_parameters(), + file_name_max_size=self.get_size_paramters(), + block_size=self.get_size_paramters(), + directory_transfer_size=self.get_size_paramters(), + read_transfer_max_size=self.get_size_paramters(), + read_transfer_multiple=self.get_size_paramters(), + read_transfer_size=self.get_size_paramters(), + write_transfer_max_size=self.get_size_paramters(), + write_transfer_multiple=self.get_size_paramters(), + write_transfer_size=self.get_size_paramters(), + max_file_size=self.get_size_paramters(), + security_flavors=dict(type='list', elements='str', choices=['unix', 'kerberos', 'kerberos_integrity', 'kerberos_privacy']), + commit_asynchronous=dict(type='bool'), + setattr_asynchronous=dict(type='bool'), + readdirplus=dict(type='bool'), + return_32bit_file_ids=dict(type='bool'), + can_set_time=dict(type='bool'), + map_lookup_uid=dict(type='bool'), + symlinks=dict(type='bool'), + write_datasync_action=self.get_sync_parameters(), + write_datasync_reply=self.get_sync_parameters(), + write_filesync_action=self.get_sync_parameters(), + write_filesync_reply=self.get_sync_parameters(), + write_unstable_action=self.get_sync_parameters(), + write_unstable_reply=self.get_sync_parameters(), + encoding=dict(type='str'), + time_delta=dict(type='dict', options=dict( + time_value=dict(type='float', required=True), + time_unit=dict(type='str', required=True, choices=['seconds', 'nanoseconds', 'milliseconds', 'microseconds']))), + access_zone=dict(type='str', default='System') + ) + + +class NFSDefaultSettingsExitHandler(): + def handle(self, nfs_default_settings_obj): + nfs_default_settings_obj.module.exit_json(**nfs_default_settings_obj.result) + + +class NFSDefaultSettingsModifyHandler(): + def handle(self, nfs_default_settings_obj): + nfs_default_settings = nfs_default_settings_obj.result["nfs_default_settings"] + if nfs_default_settings: + module_params = nfs_default_settings_obj.module.params + modify_dict = nfs_default_settings_obj.form_modify_dict(nfs_default_settings, module_params) + if modify_dict: + access_zone = nfs_default_settings_obj.module.params['access_zone'] + changed = nfs_default_settings_obj.modify_nfs_default_settings(modify_dict, access_zone) + nfs_default_settings_obj.result["changed"] = changed + nfs_default_settings_obj.result["nfs_default_settings"] = nfs_default_settings_obj.get_nfs_default_settings(access_zone) + NFSDefaultSettingsExitHandler().handle(nfs_default_settings_obj) + + +class NFSDefaultSettingsHandler(): + def handle(self, nfs_default_settings_obj): + access_zone = nfs_default_settings_obj.module.params['access_zone'] + nfs_default_settings = nfs_default_settings_obj.get_nfs_default_settings(access_zone) + nfs_default_settings_obj.result["nfs_default_settings"] = nfs_default_settings + NFSDefaultSettingsModifyHandler().handle(nfs_default_settings_obj) + + +def main(): + """ Create PowerScale NFS default settings object and perform action on it + based on user input from playbook.""" + obj = NFSDefaultSettings() + NFSDefaultSettingsHandler().handle(obj) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/nfs_global_settings.py b/plugins/modules/nfs_global_settings.py new file mode 100644 index 00000000..2dce0a73 --- /dev/null +++ b/plugins/modules/nfs_global_settings.py @@ -0,0 +1,337 @@ +#!/usr/bin/python +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Ansible module for managing NFS global settings on PowerScale""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: nfs_global_settings +version_added: '2.2.0' +short_description: Manage NFS global settings on a PowerScale Storage System +description: +- Managing NFS global settings on an PowerScale system includes retrieving details of + NFS global settings and modifying NFS global settings. + +extends_documentation_fragment: + - dellemc.powerscale.powerscale + +author: +- Trisha Datta (@trisha-dell) + +options: + service: + description: + - Specifies if the NFS service needs to be enabled or not. + type: bool + rpc_maxthreads: + description: + - Specifies the maximum number of threads in the nfsd thread pool. + type: int + rpc_minthreads: + description: + - Specifies the minimum number of threads in the nfsd thread pool. + type: int + rquota_enabled: + description: + - Enable/Disable the rquota protocol. + type: bool + nfsv3: + description: + - Enable/disable NFSv3 protocol. + type: dict + suboptions: + nfsv3_enabled: + description: + - Enable/disable NFSv3 protocol. + type: bool + nfsv3_rdma_enabled: + description: + - To enable/disable RDMA for NFSv3 protocol. + type: bool + nfsv4: + description: + - Specifies the minor versions of NFSv4 protocol. + type: dict + suboptions: + nfsv4_enabled: + description: + - Enable/disable all minor versions of NFSv4 protocol. + type: bool + nfsv40_enabled: + description: + - Enable/disable minor version 0 of NFSv4 protocol. + type: bool + nfsv41_enabled: + description: + - Enable/disable minor version 1 of NFSv4 protocol. + type: bool + nfsv42_enabled: + description: + - Enable/disable minor version 2 of NFSv4 protocol. + type: bool +notes: +- The I(check_mode) is supported. +''' + +EXAMPLES = r''' +- name: Get NFS global settings + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + +- name: Update service of NFS global settings + dellemc.powerscale.nfs_global_settings: + onefs_host: "{{ onefs_host }}" + port_no: "{{ port_no }}" + api_user: "{{ api_user }}" + api_password: "{{ api_password }}" + verify_ssl: "{{ verify_ssl }}" + service: true + nfsv3: + nfsv3_enabled: false + nfsv4: + nfsv40_enabled: true + nfsv41_enabled: true + nfsv42_enabled: false + rpc_minthreads: 17 + rpc_maxthreads: 20 + rquota_enabled: true + +''' + +RETURN = r''' +changed: + description: A boolean indicating if the task had to make changes. + returned: always + type: bool + sample: "false" +nfs_global_settings_details: + description: The updated nfs global settings details. + type: complex + returned: always + contains: + nfsv3_enabled: + description: Whether NFSv3 protocol is enabled/disabled. + type: bool + nfsv3_rdma_enabled: + description: Whether rdma is enabled for NFSv3 protocol. + type: bool + nfsv40_enabled: + description: Whether version 0 of NFSv4 protocol is enabled/disabled. + type: bool + nfsv41_enabled: + description: Whether version 1 of NFSv4 protocol is enabled/disabled. + type: bool + nfsv42_enabled: + description: Whether version 2 of NFSv4 protocol is enabled/disabled. + type: bool + nfsv4_enabled: + description: Whether NFSv4 protocol is enabled/disabled. + type: bool + rpc_maxthreads: + description: Specifies the maximum number of threads in the nfsd thread pool. + type: int + rpc_minhreads: + description: Specifies the minimum number of threads in the nfsd thread pool. + type: int + rquota_enabled: + description: Whether the rquota protocol is enabled/disabled. + type: bool + service: + description: Whether the NFS service is enabled/disabled. + type: bool + sample: { + "nfsv3_enabled": false, + "nfsv3_rdma_enabled": true, + "nfsv40_enabled": true, + "nfsv41_enabled": true, + "nfsv42_enabled": false, + "nfsv4_enabled": true, + "rpc_maxthreads": 20, + "rpc_minthreads": 17, + "rquota_enabled": true, + "service": true + } + +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +LOG = utils.get_logger('nfs_global_settings') + + +class NFSGlobalSettings: + """Class with NFS global settings operations""" + + def __init__(self): + """ Define all parameters required by this module""" + self.module_params = utils.get_powerscale_management_host_parameters() + self.module_params.update(self.get_nfs_global_settings_parameters()) + # Initialize the ansible module + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=True + ) + # Result is a dictionary that contains changed status, NFS global + # settings details + self.result = { + "changed": False, + "nfs_global_settings_details": {} + } + + PREREQS_VALIDATE = utils.validate_module_pre_reqs(self.module.params) + if PREREQS_VALIDATE \ + and not PREREQS_VALIDATE["all_packages_found"]: + self.module.fail_json( + msg=PREREQS_VALIDATE["error_message"]) + + self.api_client = utils.get_powerscale_connection(self.module.params) + self.isi_sdk = utils.get_powerscale_sdk() + LOG.info('Got python SDK instance for provisioning on PowerScale ') + check_mode_msg = f'Check mode flag is {self.module.check_mode}' + LOG.info(check_mode_msg) + + self.protocol_api = self.isi_sdk.ProtocolsApi(self.api_client) + + def get_nfs_global_settings_details(self): + """ + Get details of NFS global settings + """ + msg = "Getting NFS global settings details" + LOG.info(msg) + try: + nfs_global_obj = self.protocol_api.get_nfs_settings_global() + if nfs_global_obj: + msg = f"NFS global settings details are: {nfs_global_obj.settings.to_dict()}" + LOG.info(msg) + return nfs_global_obj.settings.to_dict() + + except Exception as e: + error_msg = f"Got error {utils.determine_error(e)} while getting" \ + f" NFS global setings details " + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def modify_nfs_global_settings(self, modify_dict): + """ + Modify the NFS global settings based on modify dict + :param modify_dict: dict containing parameters to be modfied + """ + try: + msg = "Modify NFS global settings with parameters" + LOG.info(msg) + if not self.module.check_mode: + self.protocol_api.update_nfs_settings_global( + nfs_settings_global=modify_dict) + LOG.info("Successfully modified the NFS global settings.") + return True + + except Exception as e: + error_msg = f"Modify NFS global settings failed with " \ + f"error: {utils.determine_error(e)}" + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def is_nfsv3v4_modify_required(self, settings_params, settings_details, modify_dict): + """ + Check whether modification is required in NFSv3 or NFSv4 + """ + nfsv4_keys = ["nfsv4_enabled", "nfsv40_enabled", "nfsv41_enabled", "nfsv42_enabled"] + if settings_params["nfsv4"] is not None: + for key in nfsv4_keys: + if settings_params["nfsv4"][key] is not None and \ + settings_details[key] != settings_params["nfsv4"][key]: + modify_dict[key] = settings_params["nfsv4"][key] + + nfsv3_keys = ["nfsv3_enabled", "nfsv3_rdma_enabled"] + if settings_params["nfsv3"] is not None: + for key in nfsv3_keys: + if settings_params["nfsv3"][key] is not None and \ + settings_details[key] != settings_params["nfsv3"][key]: + modify_dict[key] = settings_params["nfsv3"][key] + + return modify_dict + + def is_nfs_global_modify_required(self, settings_params, settings_details): + """ + Check whether modification is required in NFS global settings + """ + modify_dict = {} + keys = ["service", "rpc_maxthreads", "rpc_minthreads", "rquota_enabled"] + for key in keys: + if settings_params[key] is not None and \ + settings_details[key] != settings_params[key]: + modify_dict[key] = settings_params[key] + + modify_dict = self.is_nfsv3v4_modify_required(settings_params=settings_params, + settings_details=settings_details, + modify_dict=modify_dict) + + return modify_dict + + def get_nfs_global_settings_parameters(self): + return dict( + service=dict(type='bool'), rpc_maxthreads=dict(type='int'), + rpc_minthreads=dict(type='int'), + rquota_enabled=dict(type='bool'), + nfsv3=dict( + type='dict', options=dict( + nfsv3_enabled=dict(type='bool'), + nfsv3_rdma_enabled=dict(type='bool'))), + nfsv4=dict( + type='dict', options=dict( + nfsv4_enabled=dict(type='bool'), + nfsv40_enabled=dict(type='bool'), + nfsv41_enabled=dict(type='bool'), + nfsv42_enabled=dict(type='bool'))) + ) + + +class NFSGlobalSettingsExitHandler: + def handle(self, nfs_global_obj, nfs_global_details): + nfs_global_obj.result["nfs_global_settings_details"] = nfs_global_details + nfs_global_obj.module.exit_json(**nfs_global_obj.result) + + +class NFSGlobalSettingsModifyHandler: + def handle(self, nfs_global_obj, nfs_global_params, nfs_global_details): + modify_params = nfs_global_obj.is_nfs_global_modify_required(nfs_global_params, + nfs_global_details) + if modify_params: + changed = nfs_global_obj.modify_nfs_global_settings( + modify_dict=modify_params) + nfs_global_details = nfs_global_obj.get_nfs_global_settings_details() + nfs_global_obj.result["changed"] = changed + nfs_global_obj.result["nfs_global_settings_details"] = nfs_global_details + + NFSGlobalSettingsExitHandler().handle(nfs_global_obj, nfs_global_details) + + +class NFSGlobalSettingsHandler: + def handle(self, nfs_global_obj, nfs_global_params): + nfs_global_details = nfs_global_obj.get_nfs_global_settings_details() + NFSGlobalSettingsModifyHandler().handle( + nfs_global_obj=nfs_global_obj, nfs_global_params=nfs_global_params, + nfs_global_details=nfs_global_details) + + +def main(): + """ perform action on PowerScale NFS Global settings and perform action on it + based on user input from playbook.""" + obj = NFSGlobalSettings() + NFSGlobalSettingsHandler().handle(obj, obj.module.params) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/nfs_zone_settings.py b/plugins/modules/nfs_zone_settings.py new file mode 100644 index 00000000..4123b48f --- /dev/null +++ b/plugins/modules/nfs_zone_settings.py @@ -0,0 +1,333 @@ +#!/usr/bin/python +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Ansible module for managing NFS zone settings on PowerScale""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: nfs_zone_settings +version_added: '2.2.0' +short_description: Manage NFS zone settings on a PowerScale Storage System +description: +- Managing NFS zone settings on an PowerScale system includes + retrieving details and modifying NFS zone settings. + +extends_documentation_fragment: + - dellemc.powerscale.powerscale + +author: +- Bhavneet Sharma(@Bhavneet-Sharma) + +options: + access_zone: + description: + - Specifies the access zone in which the NFS zone settings apply. + type: str + default: System + nfsv4_allow_numeric_ids: + description: + - If C(true), send owner and groups as UIDs and GIDs when look up fails or + I(nfsv4_no_names) is set C(rue). + type: bool + nfsv4_domain: + description: + - Specifies the domain through which users and groups are associated. + type: str + nfsv4_no_domain: + description: + - If C(true), sends owners and groups without a domain name. + type: bool + nfsv4_no_domain_uids: + description: + - If C(true), sends UIDs and GIDs without a domain name. + type: bool + nfsv4_no_names: + description: + - If C(true), sends owners and groups as UIDs and GIDs. + type: bool + nfsv4_replace_domain: + description: + - If C(true), replaces the owner or group domain with an NFS domain name. + type: bool +notes: +- The I(check_mode) is supported. +''' + +EXAMPLES = r''' +- name: Get NFS zone settings + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + access_zone: "sample-zone" + +- name: Modify NFS zone settings + dellemc.powerscale.nfs_zone_settings: + onefs_host: "{{onefs_host}}" + api_user: "{{api_user}}" + api_password: "{{api_password}}" + verify_ssl: "{{verify_ssl}}" + access_zone: "sample-zone" + nfsv4_allow_numeric_ids: true + nfsv4_domain: "example.com" + nfsv4_no_domain: true + nfsv4_no_domain_uids: false +''' + +RETURN = r''' +changed: + description: A boolean indicating if the task had to make changes. + returned: always + type: bool + sample: "false" +nfs_zone_settings_details: + description: The NFS zone settings details. + type: dict + returned: always + contains: + nfsv4_allow_numeric_ids: + description: If C(true), sends owners and groups as UIDs and + GIDs when look up fails or if the I(nfsv4_no_names) + property is set to 1. + type: bool + nfsv4_domain: + description: Specifies the domain through which users and groups + are associated. + type: str + nfsv4_no_domain: + description: If C(true), sends owners and groups without a domain + name. + type: bool + nfsv4_no_domain_uids: + description: If C(true), sends UIDs and GIDs without a domain name. + type: bool + nfsv4_no_names: + description: If C(true), sends owners and groups as UIDs and GIDs. + type: bool + nfsv4_replace_domain: + description: If C(true), replaces the owner or group domain with an + NFS domain name. + type: bool + zone: + description: Specifies the access zone in which the NFS zone + settings apply. + type: str + sample: { + "nfsv4_allow_numeric_ids": false, + "nfsv4_domain": "", + "nfsv4_no_domain": false, + "nfsv4_no_domain_uids": false, + "nfsv4_no_names": false, + "nfsv4_replace_domain": false, + "zone": "System" + } +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +LOG = utils.get_logger('nfs_zone_settings') + + +class NFSZoneSettings: + """Class with NFS zone settings operations""" + + def __init__(self): + """ Define all parameters required by this module""" + self.module_params = utils.get_powerscale_management_host_parameters() + self.module_params.update(self.get_nfs_zone_settings_parameters()) + # Initialize the ansible module + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=True + ) + # Result is a dictionary that contains changed status, NFS zone + # settings details + self.result = { + "changed": False, + "nfs_zone_settings_details": {} + } + + # Validate the pre-requisites packages for the module + PREREQS_VALIDATE = utils.validate_module_pre_reqs(self.module.params) + if PREREQS_VALIDATE and not PREREQS_VALIDATE["all_packages_found"]: + self.module.fail_json( + msg=PREREQS_VALIDATE["error_message"]) + + # Initialize the connection to PowerScale + self.api_client = utils.get_powerscale_connection(self.module.params) + self.isi_sdk = utils.get_powerscale_sdk() + LOG.info('Got python SDK instance for provisioning on PowerScale ') + check_mode_msg = f'Check mode flag is {self.module.check_mode}' + LOG.info(check_mode_msg) + + # Initialize the APIs + self.protocol_api = self.isi_sdk.ProtocolsApi(self.api_client) + + def get_zone_settings_details(self, access_zone): + """ + Get details of an NFS zone settings for a given access zone. + :param access_zone: Access zone + :type access_zone: str + :return: NFS zone settings details + :rtype: dict + """ + msg = f"Getting NFS zone settings details for {access_zone}" \ + f" access zone" + LOG.info(msg) + try: + nfs_settings_obj = self.protocol_api.get_nfs_settings_zone( + zone=access_zone) + if nfs_settings_obj: + zone_settings = nfs_settings_obj.settings.to_dict() + + # Appending the Access zone + zone_settings["zone"] = access_zone + msg = f"NFS zone settings details are: {zone_settings}" + LOG.info(msg) + return zone_settings + + except Exception as e: + error_msg = f"Got error {utils.determine_error(e)} while getting" \ + f" NFS zone settings details for access zone" \ + f": {access_zone}" + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def _prepare_zone_settings_modify_object(self, settings_params): + """ + Prepare the NFS zone settings modify object + :param settings_params: contains params passed through playbook + :type settings_params: dict + :return: NFS zone settings modify object + :rtype: dict + """ + try: + nfs_settings_zone = self.isi_sdk.NfsSettingsZoneSettings( + nfsv4_allow_numeric_ids=settings_params[ + "nfsv4_allow_numeric_ids"], + nfsv4_domain=settings_params["nfsv4_domain"], + nfsv4_no_domain=settings_params["nfsv4_no_domain"], + nfsv4_no_domain_uids=settings_params["nfsv4_no_domain_uids"], + nfsv4_no_names=settings_params["nfsv4_no_names"], + nfsv4_replace_domain=settings_params["nfsv4_replace_domain"], + zone=settings_params["access_zone"]) + return nfs_settings_zone + except Exception as e: + error_msg = f"Got error {utils.determine_error(e)} while " \ + f"preparing NFS zone settings modify object." + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def modify_zone_settings(self, settings_params): + """ + Modify the NFS zone settings + :param settings_params: contains params passed through playbook + :type settings_params: dict + :return: True if successful + :rtype: bool + """ + zone_settings_obj = self._prepare_zone_settings_modify_object( + settings_params) + access_zone = self.module.params["access_zone"] + try: + msg = f"Modify NFS zone settings with parameters: " \ + f"{zone_settings_obj})" + LOG.info(msg) + if not self.module.check_mode: + self.protocol_api.update_nfs_settings_zone( + zone_settings_obj, zone=access_zone) + LOG.info("Successfully modified the NFS zone settings.") + return True + except Exception as e: + error_msg = f"Modify NFS zone settings with in access zone:" \ + f" {access_zone} failed with error: " \ + f"{utils.determine_error(e)}" + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def is_settings_modify_required(self, settings_params, settings_details): + """ + Check if NFS zone settings modification is required + :param settings_params: contains params passed through playbook + :param settings_details: contains details of the NFS zone settings + :return: True if NFS zone settings modification is required + """ + params = ['nfsv4_allow_numeric_ids', 'nfsv4_domain', + 'nfsv4_no_domain', 'nfsv4_no_domain_uids', 'nfsv4_no_names', + 'nfsv4_replace_domain'] + for param in params: + if settings_params[param] is not None \ + and settings_params[param] != settings_details[param]: + return True + return False + + def validate_zone_params(self): + """Validate path and access zone parameters""" + + if utils.is_param_empty_spaces(self.module.params["access_zone"]): + err_msg = "Invalid access zone provided. Provide valid access" \ + " zone." + self.module.fail_json(msg=err_msg) + + def get_nfs_zone_settings_parameters(self): + return dict( + access_zone=dict(default='System'), + nfsv4_allow_numeric_ids=dict(type='bool'), + nfsv4_domain=dict(type='str'), + nfsv4_no_domain=dict(type='bool'), + nfsv4_no_domain_uids=dict(type='bool'), + nfsv4_no_names=dict(type='bool'), + nfsv4_replace_domain=dict(type='bool')) + + +class NFSZoneSettingsExitHandler: + def handle(self, settings_obj, settings_details): + settings_obj.result["nfs_zone_settings_details"] = settings_details + settings_obj.module.exit_json(**settings_obj.result) + + +class NFSZoneSettingsModifyHandler: + def handle(self, settings_obj, settings_params, settings_details): + if settings_details: + is_modify = settings_obj.is_settings_modify_required( + settings_params, settings_details) + if is_modify: + changed = settings_obj.modify_zone_settings(settings_params) + settings_details = settings_obj.get_zone_settings_details( + access_zone=settings_params["access_zone"]) + settings_obj.result["changed"] = changed + settings_obj.result["nfs_zone_settings_details"] = \ + settings_details + + NFSZoneSettingsExitHandler().handle(settings_obj, settings_details) + + +class NFSZoneSettingsHandler: + def handle(self, settings_obj, settings_params): + settings_obj.validate_zone_params() + settings_details = settings_obj.get_zone_settings_details( + access_zone=settings_params["access_zone"]) + + NFSZoneSettingsModifyHandler().handle( + settings_obj=settings_obj, settings_params=settings_params, + settings_details=settings_details) + + +def main(): + """ Create PowerScale NFS zone settings object and perform action on it + based on user input from playbook.""" + obj = NFSZoneSettings() + NFSZoneSettingsHandler().handle(obj, obj.module.params) + + +if __name__ == '__main__': + main() diff --git a/tests/sanity/ignore-2.13.txt b/tests/sanity/ignore-2.13.txt index 7f32564d..4367ea5b 100644 --- a/tests/sanity/ignore-2.13.txt +++ b/tests/sanity/ignore-2.13.txt @@ -13,7 +13,16 @@ plugins/modules/s3_bucket.py compile-2.7 plugins/modules/s3_bucket.py compile-3.5 plugins/modules/s3_bucket.py import-2.7 plugins/modules/s3_bucket.py import-3.5 +plugins/modules/nfs_zone_settings.py compile-2.7 +plugins/modules/nfs_zone_settings.py compile-3.5 +plugins/modules/nfs_zone_settings.py import-2.7 +plugins/modules/nfs_zone_settings.py import-3.5 +plugins/modules/info.py compile-2.7 +plugins/modules/info.py compile-3.5 +plugins/modules/info.py import-2.7 +plugins/modules/info.py import-3.5 plugins/modules/s3_bucket.py validate-modules:missing-gplv3-license +plugins/modules/nfs_zone_settings.py validate-modules:missing-gplv3-license plugins/modules/info.py validate-modules:missing-gplv3-license plugins/modules/group.py validate-modules:missing-gplv3-license plugins/modules/ldap.py validate-modules:missing-gplv3-license @@ -71,3 +80,21 @@ plugins/modules/networkpool.py import-3.5 plugins/modules/networkpool.py compile-2.7 plugins/modules/networkpool.py compile-3.5 plugins/modules/networkpool.py validate-modules:missing-gplv3-license +plugins/modules/nfs_global_settings.py import-2.7 +plugins/modules/nfs_global_settings.py import-3.5 +plugins/modules/nfs_global_settings.py compile-2.7 +plugins/modules/nfs_global_settings.py compile-3.5 +plugins/modules/nfs_global_settings.py validate-modules:missing-gplv3-license +plugins/modules/nfs_default_settings.py import-2.7 +plugins/modules/nfs_default_settings.py import-3.5 +plugins/modules/nfs_default_settings.py compile-2.7 +plugins/modules/nfs_default_settings.py compile-3.5 +plugins/modules/nfs_default_settings.py validate-modules:missing-gplv3-license +plugins/module_utils/storage/dell/shared_library/auth.py compile-2.7 +plugins/module_utils/storage/dell/shared_library/auth.py compile-3.5 +plugins/module_utils/storage/dell/shared_library/auth.py import-2.7 +plugins/module_utils/storage/dell/shared_library/auth.py import-3.5 +plugins/module_utils/storage/dell/shared_library/protocol.py compile-2.7 +plugins/module_utils/storage/dell/shared_library/protocol.py compile-3.5 +plugins/module_utils/storage/dell/shared_library/protocol.py import-2.7 +plugins/module_utils/storage/dell/shared_library/protocol.py import-3.5 diff --git a/tests/sanity/ignore-2.14.txt b/tests/sanity/ignore-2.14.txt index 7f32564d..4367ea5b 100644 --- a/tests/sanity/ignore-2.14.txt +++ b/tests/sanity/ignore-2.14.txt @@ -13,7 +13,16 @@ plugins/modules/s3_bucket.py compile-2.7 plugins/modules/s3_bucket.py compile-3.5 plugins/modules/s3_bucket.py import-2.7 plugins/modules/s3_bucket.py import-3.5 +plugins/modules/nfs_zone_settings.py compile-2.7 +plugins/modules/nfs_zone_settings.py compile-3.5 +plugins/modules/nfs_zone_settings.py import-2.7 +plugins/modules/nfs_zone_settings.py import-3.5 +plugins/modules/info.py compile-2.7 +plugins/modules/info.py compile-3.5 +plugins/modules/info.py import-2.7 +plugins/modules/info.py import-3.5 plugins/modules/s3_bucket.py validate-modules:missing-gplv3-license +plugins/modules/nfs_zone_settings.py validate-modules:missing-gplv3-license plugins/modules/info.py validate-modules:missing-gplv3-license plugins/modules/group.py validate-modules:missing-gplv3-license plugins/modules/ldap.py validate-modules:missing-gplv3-license @@ -71,3 +80,21 @@ plugins/modules/networkpool.py import-3.5 plugins/modules/networkpool.py compile-2.7 plugins/modules/networkpool.py compile-3.5 plugins/modules/networkpool.py validate-modules:missing-gplv3-license +plugins/modules/nfs_global_settings.py import-2.7 +plugins/modules/nfs_global_settings.py import-3.5 +plugins/modules/nfs_global_settings.py compile-2.7 +plugins/modules/nfs_global_settings.py compile-3.5 +plugins/modules/nfs_global_settings.py validate-modules:missing-gplv3-license +plugins/modules/nfs_default_settings.py import-2.7 +plugins/modules/nfs_default_settings.py import-3.5 +plugins/modules/nfs_default_settings.py compile-2.7 +plugins/modules/nfs_default_settings.py compile-3.5 +plugins/modules/nfs_default_settings.py validate-modules:missing-gplv3-license +plugins/module_utils/storage/dell/shared_library/auth.py compile-2.7 +plugins/module_utils/storage/dell/shared_library/auth.py compile-3.5 +plugins/module_utils/storage/dell/shared_library/auth.py import-2.7 +plugins/module_utils/storage/dell/shared_library/auth.py import-3.5 +plugins/module_utils/storage/dell/shared_library/protocol.py compile-2.7 +plugins/module_utils/storage/dell/shared_library/protocol.py compile-3.5 +plugins/module_utils/storage/dell/shared_library/protocol.py import-2.7 +plugins/module_utils/storage/dell/shared_library/protocol.py import-3.5 diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 7f32564d..02dfffae 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -13,7 +13,12 @@ plugins/modules/s3_bucket.py compile-2.7 plugins/modules/s3_bucket.py compile-3.5 plugins/modules/s3_bucket.py import-2.7 plugins/modules/s3_bucket.py import-3.5 +plugins/modules/nfs_zone_settings.py compile-2.7 +plugins/modules/nfs_zone_settings.py compile-3.5 +plugins/modules/nfs_zone_settings.py import-2.7 +plugins/modules/nfs_zone_settings.py import-3.5 plugins/modules/s3_bucket.py validate-modules:missing-gplv3-license +plugins/modules/nfs_zone_settings.py validate-modules:missing-gplv3-license plugins/modules/info.py validate-modules:missing-gplv3-license plugins/modules/group.py validate-modules:missing-gplv3-license plugins/modules/ldap.py validate-modules:missing-gplv3-license @@ -21,6 +26,10 @@ plugins/modules/nfs.py compile-2.7 plugins/modules/nfs.py compile-3.5 plugins/modules/nfs.py import-2.7 plugins/modules/nfs.py import-3.5 +plugins/modules/info.py compile-2.7 +plugins/modules/info.py compile-3.5 +plugins/modules/info.py import-2.7 +plugins/modules/info.py import-3.5 plugins/modules/nfs.py validate-modules:missing-gplv3-license plugins/modules/node.py validate-modules:missing-gplv3-license plugins/modules/smartquota.py validate-modules:missing-gplv3-license @@ -71,3 +80,21 @@ plugins/modules/networkpool.py import-3.5 plugins/modules/networkpool.py compile-2.7 plugins/modules/networkpool.py compile-3.5 plugins/modules/networkpool.py validate-modules:missing-gplv3-license +plugins/modules/nfs_global_settings.py import-2.7 +plugins/modules/nfs_global_settings.py import-3.5 +plugins/modules/nfs_global_settings.py compile-2.7 +plugins/modules/nfs_global_settings.py compile-3.5 +plugins/modules/nfs_global_settings.py validate-modules:missing-gplv3-license +plugins/modules/nfs_default_settings.py import-2.7 +plugins/modules/nfs_default_settings.py import-3.5 +plugins/modules/nfs_default_settings.py compile-2.7 +plugins/modules/nfs_default_settings.py compile-3.5 +plugins/modules/nfs_default_settings.py validate-modules:missing-gplv3-license +plugins/module_utils/storage/dell/shared_library/auth.py compile-2.7 +plugins/module_utils/storage/dell/shared_library/auth.py compile-3.5 +plugins/module_utils/storage/dell/shared_library/auth.py import-2.7 +plugins/module_utils/storage/dell/shared_library/auth.py import-3.5 +plugins/module_utils/storage/dell/shared_library/protocol.py compile-2.7 +plugins/module_utils/storage/dell/shared_library/protocol.py compile-3.5 +plugins/module_utils/storage/dell/shared_library/protocol.py import-2.7 +plugins/module_utils/storage/dell/shared_library/protocol.py import-3.5 diff --git a/tests/unit/plugins/module_utils/mock_info_api.py b/tests/unit/plugins/module_utils/mock_info_api.py index 9886ec81..6bf60c90 100644 --- a/tests/unit/plugins/module_utils/mock_info_api.py +++ b/tests/unit/plugins/module_utils/mock_info_api.py @@ -2,7 +2,7 @@ # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) -"""Mock Api response for Unit tests of Gatherfacts module on PowerScale""" +"""Mock Api response for Unit tests of Info module on PowerScale""" from __future__ import (absolute_import, division, print_function) @@ -69,7 +69,10 @@ class MockGatherfactsApi: 'UserMappingRules': [ ], 'LdapProviders': [ - ] + ], + 'NfsZoneSettings': {}, + 'NfsDefaultSettings': {}, + 'NfsGlobalSettings': {} } @staticmethod @@ -949,3 +952,15 @@ def get_ldap_details_response(response_type): else: return "Getting list of ldap providers for PowerScale: %s failed with error: SDK Error message" % ( MockGatherfactsApi.GATHERFACTS_COMMON_ARGS['onefs_host']) + + @staticmethod + def get_nfs_zone_setting_response(response_type): + if response_type == "error": + return "Getting zone settings for PowerScale: %s failed with " \ + "error: SDK Error message" % (MockGatherfactsApi.GATHERFACTS_COMMON_ARGS['onefs_host']) + + @staticmethod + def get_nfs_global_setting_response(response_type): + if response_type == "error": + return "Getting NFS global settings for PowerScale: %s failed with " \ + "error: SDK Error message" % (MockGatherfactsApi.GATHERFACTS_COMMON_ARGS['onefs_host']) diff --git a/tests/unit/plugins/module_utils/mock_nfs_export_api.py b/tests/unit/plugins/module_utils/mock_nfs_export_api.py index b294e670..fa05e35d 100644 --- a/tests/unit/plugins/module_utils/mock_nfs_export_api.py +++ b/tests/unit/plugins/module_utils/mock_nfs_export_api.py @@ -23,6 +23,8 @@ "access_zone": None, "clients": None, "root_clients": None, + "map_root": None, + "map_non_root": None, "read_only_clients": None, "read_write_clients": None, "client_state": None, @@ -54,6 +56,34 @@ "security_flavors": [ "krb5" ], + "map_root": { + "enabled": True, + "primary_group": { + "id": "GROUP:group1", + "name": None, + "type": None + }, + "secondary_groups": [], + "user": { + "id": "USER:user", + "name": None, + "type": None + } + }, + "map_non_root": { + "enabled": True, + "primary_group": { + "id": "GROUP:group1", + "name": None, + "type": None + }, + "secondary_groups": [], + "user": { + "id": "USER:root", + "name": None, + "type": None + } + }, "snapshot": None, "zone": SYS_ZONE}]} @@ -140,7 +170,9 @@ "read_only_clients": [SAMPLE_IP1], "clients": [SAMPLE_IP1], "client_state": "present-in-export", - "security_flavors": ["kerberos"] + "security_flavors": ["kerberos"], + "map_root": {"user": "root", "primary_group": "root", "secondary_groups": [{"name": "group1"}, {"name": "group2"}]}, + "map_non_root": {"user": "root", "primary_group": "root", "secondary_groups": [{"name": "group1"}, {"name": "group2"}]} } MODIFY_NFS_PARAMS = { @@ -155,9 +187,32 @@ "client_state": "present-in-export", "sub_directories_mountable": True, "security_flavors": ["unix", "kerberos_privacy"], + "map_root": {"user": "root", "primary_group": "root", "secondary_groups": [{"name": "group1", "state": "absent"}, {"name": "group2"}]}, + "map_non_root": {"user": "root", "primary_group": "root", "secondary_groups": [{"name": "group1"}, {"name": "group2"}]} } +class NFSTestExportObj: + export_obj = None + + def __init__(self, obj): + self.export_obj = obj + + @staticmethod + def to_dict(): + return NFSTestExportObj.export_obj + + +class NFSTestExport: + total = 0 + exports = [] + + def __init__(self, total=0, export_obj=None): + self.total = total + if export_obj: + self.exports.append(NFSTestExportObj(export_obj)) + + def get_nfs_failed_msg(): return 'Got error SDK Error message while getting NFS export details for path' diff --git a/tests/unit/plugins/module_utils/mock_nfs_global_settings_api.py b/tests/unit/plugins/module_utils/mock_nfs_global_settings_api.py new file mode 100644 index 00000000..264a1cff --- /dev/null +++ b/tests/unit/plugins/module_utils/mock_nfs_global_settings_api.py @@ -0,0 +1,43 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Mock Api response for Unit tests of NFS global settings module on PowerScale""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockNFSGlobalSettingsApi: + MODULE_UTILS_PATH = 'ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.utils' + + NFS_GLOBAL_COMMON_ARGS = { + "onefs_host": "**.***.**.***", + "nfsv3_enabled": None, + "nfsv3": None, + "nfsv4": None, + "rpc_maxthreads": 20, + "rpc_minthreads": 17, + "rquota_enabled": None, + "service": None + } + GET_NFS_GLOBAL_RESPONSE = { + "nfsv3_enabled": False, + "nfsv3_rdma_enabled": True, + "nfsv40_enabled": True, + "nfsv41_enabled": True, + "nfsv42_enabled": False, + "nfsv4_enabled": True, + "rpc_maxthreads": 20, + "rpc_minthreads": 17, + "rquota_enabled": True, + "service": True + } + + @staticmethod + def get_nfs_global_settings_exception_response(response_type): + if response_type == 'get_details_exception': + return "Got error SDK Error message while getting NFS global setings details " + elif response_type == 'update_exception': + return "Modify NFS global settings failed with error: SDK Error message" diff --git a/tests/unit/plugins/module_utils/mock_nfs_zone_settings.py b/tests/unit/plugins/module_utils/mock_nfs_zone_settings.py new file mode 100644 index 00000000..58d4aa07 --- /dev/null +++ b/tests/unit/plugins/module_utils/mock_nfs_zone_settings.py @@ -0,0 +1,54 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Mock Api response for Unit tests of NFS Zone Settings module on PowerScale""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockNFSZoneSettingsApi: + MODULE_UTILS_PATH = 'ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.utils' + ZONE = "sample-zone" + NFS_DOMAIN = "example.com" + + ZONE_SETTINGS_COMMON_ARGS = { + "onefs_host": "**.***.**.***", + "access_zone": None, + "nfsv4_allow_numeric_ids": None, + "nfsv4_domain": None, + "nfsv4_no_domain": None, + "nfsv4_no_domain_uids": None, + "nfsv4_no_names": None, + "nfsv4_replace_domain": None + } + + GET_NFS_ZONE_SETTINGS_RESPONSE = { + "settings": { + "nfsv4_allow_numeric_ids": True, + "nfsv4_domain": None, + "nfsv4_no_domain": True, + "nfsv4_no_domain_uids": True, + "nfsv4_no_names": True, + "nfsv4_replace_domain": True, + "zone": "sample-zone" + } + } + + @staticmethod + def zone_settings_exception(response_type): + if response_type == "get_settings_exception": + return 'Got error SDK Error message while getting NFS zone ' \ + 'settings details for access zone: sample-zone' + elif response_type == "update_zone_settings_exception": + return 'Modify NFS zone settings with in access zone: ' \ + 'sample-zone failed with error: SDK Error message' + elif response_type == "prepare_zone_settings_object_exception": + return 'Got error SDK Error message while preparing NFS zone' \ + ' settings modify object' + elif response_type == "invalid_zone_exception": + return 'Invalid access zone provided. Provide valid access zone' + elif response_type == "pre_reqs_exception": + return '**********' diff --git a/tests/unit/plugins/module_utils/mock_nfsdefaultsettings_api.py b/tests/unit/plugins/module_utils/mock_nfsdefaultsettings_api.py new file mode 100644 index 00000000..ee4bd260 --- /dev/null +++ b/tests/unit/plugins/module_utils/mock_nfsdefaultsettings_api.py @@ -0,0 +1,91 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Mock Api response for Unit tests of nfs default settings module on PowerScale""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockNfsDefaultSettingsApi: + MODULE_UTILS_PATH = 'ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.utils' + NFS_DEFAULT_SETTINGS_COMMON_ARGS = { + 'onefs_host': '**.***.**.***', + 'access_zone': 'System', + 'map_root': {}, + 'map_non_root': {}, + 'map_failure': {}, + 'file_name_max_size': None, + 'block_size': None, + 'commit_asynchronous': None, + 'directory_transfer_size': None, + 'read_transfer_max_size': None, + 'read_transfer_multiple': None, + 'read_transfer_size': None, + 'setattr_asynchronous': None, + 'write_datasync_action': None, + 'write_datasync_reply': None, + 'write_filesync_action': None, + 'write_filesync_reply': None, + 'write_transfer_max_size': None, + 'write_transfer_multiple': None, + 'write_transfer_size': None, + 'write_unstable_action': None, + 'write_unstable_reply': None, + 'max_file_size': None, + 'readdirplus': None, + 'return_32bit_file_ids': None, + 'can_set_time': None, + 'encoding': None, + 'map_lookup_uid': None, + 'symlinks': None, + 'time_delta': None, + 'security_flavors': [] + } + GET_NFSDEFAULTSETTINGS_RESPONSE = { + "commit_asynchronous": False, + "encoding": "UTF-8", + "map_full": True, + "map_root": { + "enabled": False, + "primary_group": { + "id": "GROUP:0001", + "name": None, + "type": None + }, + "secondary_groups": [ + { + "id": "GROUP:0002" + }, + { + "id": "GROUP:0003" + }, + { + "id": "GROUP:0004" + } + ], + "user": { + "id": "USER:0005", + "name": None, + "type": None + } + }, + "max_file_size": 3145728, + "name_max_size": 10009, + "security_flavors": [ + "unix", + "krb5" + ], + "time_delta": 1.0, + "write_datasync_action": "DATASYNC", + "zone": "System" + } + + @staticmethod + def get_nfsdefaultsettings_exception_response(response_type): + if response_type == 'update_exception': + return "Modifying NFS default settings for access zone: System failed with error: SDK Error message" + elif response_type == 'form_dict_exception': + return "Forming modification dict failed with error: " diff --git a/tests/unit/plugins/modules/test_info.py b/tests/unit/plugins/modules/test_info.py index 03b238a8..a6a03490 100644 --- a/tests/unit/plugins/modules/test_info.py +++ b/tests/unit/plugins/modules/test_info.py @@ -279,3 +279,23 @@ def test_get_ldap_details_api_exception(self, gatherfacts_module_mock): gatherfacts_module_mock.auth_api.list_providers_ldap = MagicMock(side_effect=MockApiException) gatherfacts_module_mock.perform_module_operation() assert MockGatherfactsApi.get_ldap_details_response('error') == gatherfacts_module_mock.module.fail_json.call_args[1]['msg'] + + def test_get_nfs_zone_setting_exception(self, gatherfacts_module_mock): + self.get_module_args.update({ + 'gather_subset': ['nfs_zone_settings'] + }) + gatherfacts_module_mock.module.params = self.get_module_args + gatherfacts_module_mock.protocol_api = MagicMock() + gatherfacts_module_mock.protocol_api.get_nfs_settings_zone = MagicMock(side_effect=MockApiException) + gatherfacts_module_mock.perform_module_operation() + assert MockGatherfactsApi.get_nfs_zone_setting_response('error') == gatherfacts_module_mock.module.fail_json.call_args[1]['msg'] + + def test_get_nfs_global_setting_exception(self, gatherfacts_module_mock): + self.get_module_args.update({ + 'gather_subset': ['nfs_global_settings'] + }) + gatherfacts_module_mock.module.params = self.get_module_args + gatherfacts_module_mock.protocol_api = MagicMock() + gatherfacts_module_mock.protocol_api.get_nfs_settings_global = MagicMock(side_effect=MockApiException) + gatherfacts_module_mock.perform_module_operation() + assert MockGatherfactsApi.get_nfs_global_setting_response('error') == gatherfacts_module_mock.module.fail_json.call_args[1]['msg'] diff --git a/tests/unit/plugins/modules/test_nfs_export.py b/tests/unit/plugins/modules/test_nfs_export.py index e8b10cc9..210fdc84 100644 --- a/tests/unit/plugins/modules/test_nfs_export.py +++ b/tests/unit/plugins/modules/test_nfs_export.py @@ -15,6 +15,7 @@ utils.get_logger = MagicMock() utils.isi_sdk = MagicMock() +utils.get_nfs_map_object = MagicMock() from ansible.module_utils import basic basic.AnsibleModule = MagicMock() @@ -22,10 +23,14 @@ from ansible_collections.dellemc.powerscale.plugins.modules.nfs import NfsExport from ansible_collections.dellemc.powerscale.tests.unit.plugins.\ module_utils import mock_nfs_export_api as MockNFSApi +from ansible_collections.dellemc.powerscale.tests.unit.plugins.\ + module_utils.mock_nfs_export_api import NFSTestExport from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_sdk_response \ import MockSDKResponse from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_api_exception \ import MockApiException +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json class TestNfsExport(): @@ -37,6 +42,7 @@ def nfs_module_mock(self, mocker): nfs_module_mock = NfsExport() nfs_module_mock.module = MagicMock() nfs_module_mock.module.check_mode = False + nfs_module_mock.module.fail_json = fail_json return nfs_module_mock def test_get_nfs_response(self, nfs_module_mock): @@ -45,7 +51,8 @@ def test_get_nfs_response(self, nfs_module_mock): "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( - return_value=MockSDKResponse(MockNFSApi.NFS_1)) + return_value=NFSTestExport(1, MockNFSApi.NFS_1['exports']) + ) nfs_module_mock.perform_module_operation() nfs_module_mock.protocol_api.list_nfs_exports.assert_called() @@ -55,7 +62,8 @@ def test_get_nfs_root_response(self, nfs_module_mock): "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( - return_value=MockNFSApi.NFS_1) + return_value=NFSTestExport(1, MockNFSApi.NFS_1['exports']) + ) nfs_module_mock.perform_module_operation() nfs_module_mock.protocol_api.list_nfs_exports.assert_called() @@ -68,18 +76,15 @@ def test_get_nfs_response_using_id(self, nfs_module_mock): return_value=None) nfs_module_mock.protocol_api.get_nfs_export = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - nfs_module_mock.protocol_api.get_nfs_export.assert_called() def test_get_nfs_response_multiple_path(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, "access_zone": MockNFSApi.SYS_ZONE, "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args - nfs_module_mock.protocol_api.list_nfs_exports.to_dict = MagicMock( - return_value=MockNFSApi.NFS_MULTIPLE) - nfs_module_mock.perform_module_operation() - nfs_module_mock.protocol_api.get_nfs_export.assert_called() + nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( + return_value=NFSTestExport(2, MockNFSApi.NFS_MULTIPLE['exports']) + ) def test_get_nfs_non_system_az_response(self, nfs_module_mock): self.get_nfs_args.update({"path": "/sample_file_path1", @@ -89,7 +94,8 @@ def test_get_nfs_non_system_az_response(self, nfs_module_mock): nfs_module_mock.zone_summary_api.get_zones_summary_zone.to_dict = MagicMock( return_value=MockNFSApi.ZONE) nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( - return_value=MockSDKResponse(MockNFSApi.NFS_1)) + return_value=NFSTestExport(1, MockNFSApi.NFS_1['exports']) + ) nfs_module_mock.perform_module_operation() nfs_module_mock.protocol_api.list_nfs_exports.assert_called() @@ -99,10 +105,12 @@ def test_get_nfs_non_system_az_exception(self, nfs_module_mock): "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args MockApiException.status = '404' + nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( + return_value=NFSTestExport(1, MockNFSApi.NFS_1['exports']) + ) nfs_module_mock.zone_summary_api.get_zones_summary_zone.to_dict = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - nfs_module_mock.protocol_api.list_nfs_exports.assert_called() + self.capture_fail_json_call(MockNFSApi.get_nfs_failed_msg(), nfs_module_mock) def test_get_nfs_404_exception(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, @@ -112,10 +120,7 @@ def test_get_nfs_404_exception(self, nfs_module_mock): MockApiException.status = '404' nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - assert MockNFSApi.get_nfs_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] - nfs_module_mock.protocol_api.list_nfs_exports.assert_called() + self.capture_fail_json_call(MockNFSApi.get_nfs_failed_msg(), nfs_module_mock) def operation_before_create(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, @@ -126,10 +131,15 @@ def operation_before_create(self, nfs_module_mock): "clients": [MockNFSApi.SAMPLE_IP1], "client_state": "present-in-export", "security_flavors": ["kerberos"], + "map_root": {"enabled": True, "user": "root", "primary_group": "root", + "secondary_groups": [{"name": "group1", "state": "absent"}, {"name": "group2"}]}, + "map_non_root": {"enabled": True, "user": "root", "primary_group": "root", + "secondary_groups": [{"name": "group1"}, {"name": "group2", "state": "absent"}]}, "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( - return_value=None) + return_value=NFSTestExport() + ) def test_create_nfs_response(self, nfs_module_mock): self.operation_before_create(nfs_module_mock) @@ -142,24 +152,18 @@ def test_create_nfs_response(self, nfs_module_mock): def test_create_nfs_exception(self, nfs_module_mock): self.operation_before_create(nfs_module_mock) - nfs_module_mock.isi_sdk.NfsExportCreateParams = MagicMock( - return_value=MockNFSApi.NFS_1) + nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( + return_value=NFSTestExport(1, MockNFSApi.NFS_1['exports']) + ) nfs_module_mock.protocol_api.create_nfs_export = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - nfs_module_mock.isi_sdk.NfsExportCreateParams.assert_called() - nfs_module_mock.protocol_api.create_nfs_export.assert_called() - assert MockNFSApi.create_nfs_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockNFSApi.create_nfs_failed_msg(), nfs_module_mock) def test_create_nfs_params_exception(self, nfs_module_mock): self.operation_before_create(nfs_module_mock) nfs_module_mock.isi_sdk.NfsExportCreateParams = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - nfs_module_mock.isi_sdk.NfsExportCreateParams.assert_called() - assert MockNFSApi.create_nfs_param_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockNFSApi.create_nfs_param_failed_msg(), nfs_module_mock) def test_create_nfs_without_clients(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, @@ -171,10 +175,9 @@ def test_create_nfs_without_clients(self, nfs_module_mock): "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( - return_value=None) - nfs_module_mock.perform_module_operation() - assert MockNFSApi.without_clients_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] + return_value=NFSTestExport() + ) + self.capture_fail_json_call(MockNFSApi.without_clients_failed_msg(), nfs_module_mock) def test_create_nfs_without_client_state(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, @@ -185,10 +188,9 @@ def test_create_nfs_without_client_state(self, nfs_module_mock): "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args nfs_module_mock.protocol_api.list_nfs_exports = MagicMock( - return_value=None) - nfs_module_mock.perform_module_operation() - assert MockNFSApi.without_client_state_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] + return_value=NFSTestExport() + ) + self.capture_fail_json_call(MockNFSApi.without_client_state_failed_msg(), nfs_module_mock) def operation_before_modify(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, @@ -202,6 +204,9 @@ def operation_before_modify(self, nfs_module_mock): "client_state": "present-in-export", "sub_directories_mountable": True, "security_flavors": ["unix", "kerberos_privacy"], + "map_root": {"enabled": True, "user": "root", "primary_group": "root", + "secondary_groups": [{"name": "group1", "state": "absent"}, {"name": "group2"}]}, + "map_non_root": {"enabled": False}, "state": "present"}) nfs_module_mock.module.params = self.get_nfs_args nfs_module_mock.get_nfs_export = MagicMock( @@ -219,10 +224,7 @@ def test_modify_nfs_response_exception(self, nfs_module_mock): self.operation_before_modify(nfs_module_mock) nfs_module_mock.protocol_api.update_nfs_export = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - nfs_module_mock.protocol_api.update_nfs_export.assert_called() - assert MockNFSApi.modify_nfs_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockNFSApi.modify_nfs_failed_msg(), nfs_module_mock) def test_remove_clients_nfs(self, nfs_module_mock): self.get_nfs_args.update({"path": MockNFSApi.PATH_1, @@ -264,7 +266,10 @@ def test_delete_nfs_response_exception(self, nfs_module_mock): return_value=MockNFSApi.NFS_2['exports'][0]) nfs_module_mock.protocol_api.delete_nfs_export = MagicMock( side_effect=utils.ApiException) - nfs_module_mock.perform_module_operation() - nfs_module_mock.protocol_api.delete_nfs_export.assert_called() - assert MockNFSApi.delete_nfs_failed_msg() in \ - nfs_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockNFSApi.delete_nfs_failed_msg(), nfs_module_mock) + + def capture_fail_json_call(self, error_msg, nfs_module_mock): + try: + nfs_module_mock.perform_module_operation() + except FailJsonException as fj_object: + assert error_msg in fj_object.message diff --git a/tests/unit/plugins/modules/test_nfs_global_settings.py b/tests/unit/plugins/modules/test_nfs_global_settings.py new file mode 100644 index 00000000..11115af6 --- /dev/null +++ b/tests/unit/plugins/modules/test_nfs_global_settings.py @@ -0,0 +1,103 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for NFS global settings module on PowerScale""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +from mock.mock import MagicMock +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +utils.get_logger = MagicMock() +utils.isi_sdk = MagicMock() +PREREQS_VALIDATE = { + "all_packages_found": True +} +utils.validate_module_pre_reqs = MagicMock(return_value=PREREQS_VALIDATE) +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() + +from ansible_collections.dellemc.powerscale.plugins.modules.nfs_global_settings import NFSGlobalSettings +from ansible_collections.dellemc.powerscale.plugins.modules.nfs_global_settings import NFSGlobalSettingsHandler +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_nfs_global_settings_api \ + import MockNFSGlobalSettingsApi +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json + + +class TestNFSGlobalSettings(): + nfs_global_args = MockNFSGlobalSettingsApi.NFS_GLOBAL_COMMON_ARGS + + @pytest.fixture + def nfs_global_module_mock(self, mocker): + mocker.patch(MockNFSGlobalSettingsApi.MODULE_UTILS_PATH + '.ApiException', new=MockApiException) + nfs_global_module_mock = NFSGlobalSettings() + nfs_global_module_mock.module.check_mode = False + nfs_global_module_mock.module.fail_json = fail_json + return nfs_global_module_mock + + def capture_fail_json_call(self, error_msg, nfs_global_module_mock): + try: + NFSGlobalSettingsHandler().handle(nfs_global_module_mock, nfs_global_module_mock.module.params) + except FailJsonException as fj_object: + assert error_msg in fj_object.message + + def test_get_nfs_global_details(self, nfs_global_module_mock): + self.nfs_global_args.update({ + }) + nfs_global_module_mock.module.params = self.nfs_global_args + NFSGlobalSettingsHandler().handle(nfs_global_module_mock, nfs_global_module_mock.module.params) + nfs_global_module_mock.protocol_api.get_nfs_settings_global.assert_called() + + def test_get_nfs_global_details_exception(self, nfs_global_module_mock): + self.nfs_global_args.update({}) + nfs_global_module_mock.module.params = self.nfs_global_args + nfs_global_module_mock.protocol_api.get_nfs_settings_global = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockNFSGlobalSettingsApi.get_nfs_global_settings_exception_response('get_details_exception'), nfs_global_module_mock) + + def test_modify_nfs_global_response(self, nfs_global_module_mock): + self.nfs_global_args.update({ + "service": True, + "nfsv3": {"nfsv3_enabled": True, "nfsv3_rdma_enabled": None}, + "nfsv4": { + "nfsv4_enabled": True, + "nfsv40_enabled": False, + "nfsv41_enabled": None, + "nfsv42_enabled": True + } + }) + nfs_global_module_mock.module.params = self.nfs_global_args + nfs_global_module_mock.get_nfs_global_settings_details = MagicMock( + return_value=MockNFSGlobalSettingsApi.GET_NFS_GLOBAL_RESPONSE) + NFSGlobalSettingsHandler().handle(nfs_global_module_mock, + nfs_global_module_mock.module.params) + assert nfs_global_module_mock.module.exit_json.call_args[1]['changed'] is True + nfs_global_module_mock.protocol_api.update_nfs_settings_global.assert_called() + + def test_modify_nfs_global_exception(self, nfs_global_module_mock): + self.nfs_global_args.update({ + "service": True, + "nfsv3": {"nfsv3_enabled": True, "nfsv3_rdma_enabled": None}, + "nfsv4": { + "nfsv4_enabled": True, + "nfsv40_enabled": False, + "nfsv41_enabled": None, + "nfsv42_enabled": True + } + }) + nfs_global_module_mock.module.params = self.nfs_global_args + nfs_global_module_mock.get_nfs_global_settings_details = MagicMock( + return_value=MockNFSGlobalSettingsApi.GET_NFS_GLOBAL_RESPONSE) + nfs_global_module_mock.protocol_api.update_nfs_settings_global = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockNFSGlobalSettingsApi.get_nfs_global_settings_exception_response('update_exception'), nfs_global_module_mock) diff --git a/tests/unit/plugins/modules/test_nfs_zone_settings.py b/tests/unit/plugins/modules/test_nfs_zone_settings.py new file mode 100644 index 00000000..72e1c92b --- /dev/null +++ b/tests/unit/plugins/modules/test_nfs_zone_settings.py @@ -0,0 +1,140 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for NFS Zone Settings module on PowerScale""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +from mock.mock import MagicMock +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +utils.get_logger = MagicMock() +utils.isi_sdk = MagicMock() +PREREQS_VALIDATE = { + "all_packages_found": True +} +utils.validate_module_pre_reqs = MagicMock(return_value=PREREQS_VALIDATE) +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() + +from ansible_collections.dellemc.powerscale.plugins.modules.nfs_zone_settings \ + import NFSZoneSettings +from ansible_collections.dellemc.powerscale.plugins.modules.nfs_zone_settings \ + import NFSZoneSettingsHandler +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_nfs_zone_settings \ + import MockNFSZoneSettingsApi +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json + + +class TestNFSZoneSettings(): + nfs_settings_args = MockNFSZoneSettingsApi.ZONE_SETTINGS_COMMON_ARGS + + @pytest.fixture + def nfs_settings_module_mock(self, mocker): + mocker.patch(MockNFSZoneSettingsApi.MODULE_UTILS_PATH + '.ApiException', new=MockApiException) + nfs_settings_module_mock = NFSZoneSettings() + nfs_settings_module_mock.module.check_mode = False + nfs_settings_module_mock.module.fail_json = fail_json + return nfs_settings_module_mock + + def capture_fail_json_call(self, error_msg, nfs_settings_module_mock): + try: + NFSZoneSettingsHandler().handle(nfs_settings_module_mock, nfs_settings_module_mock.module.params) + except FailJsonException as fj_object: + assert error_msg in fj_object.message + + def test_get_nfs_zone_settings(self, nfs_settings_module_mock): + self.nfs_settings_args.update({ + "access_zone": MockNFSZoneSettingsApi.ZONE + }) + nfs_settings_module_mock.module.params = self.nfs_settings_args + NFSZoneSettingsHandler().handle(nfs_settings_module_mock, nfs_settings_module_mock.module.params) + assert nfs_settings_module_mock.module.exit_json.call_args[1]['changed'] is False + nfs_settings_module_mock.protocol_api.get_nfs_settings_zone.assert_called() + + def test_get_nfs_zone_settings_exception(self, nfs_settings_module_mock): + self.nfs_settings_args.update({ + "access_zone": MockNFSZoneSettingsApi.ZONE + }) + nfs_settings_module_mock.module.params = self.nfs_settings_args + nfs_settings_module_mock.protocol_api.get_nfs_settings_zone = MagicMock(side_effect=MockApiException) + self.capture_fail_json_call( + MockNFSZoneSettingsApi.zone_settings_exception( + response_type="get_settings_exception"), nfs_settings_module_mock) + + def test_update_nfs_zone_settings(self, nfs_settings_module_mock): + self.nfs_settings_args.update({ + "access_zone": MockNFSZoneSettingsApi.ZONE, + "nfsv4_allow_numeric_ids": False, + "nfsv4_domain": MockNFSZoneSettingsApi.NFS_DOMAIN, + "nfsv4_no_domain": False, + "nfsv4_no_domain_uids": False, + "nfsv4_no_names": False, + "nfsv4_replace_domain": False + }) + nfs_settings_module_mock.module.params = self.nfs_settings_args + nfs_settings_module_mock.protocol_api.get_nfs_settings_zone.to_dict = MagicMock( + return_value=MockNFSZoneSettingsApi.GET_NFS_ZONE_SETTINGS_RESPONSE) + NFSZoneSettingsHandler().handle(nfs_settings_module_mock, nfs_settings_module_mock.module.params) + assert nfs_settings_module_mock.module.exit_json.call_args[1]['changed'] is True + nfs_settings_module_mock.protocol_api.update_nfs_settings_zone.assert_called() + + def test_update_nfs_zone_settings_exception(self, nfs_settings_module_mock): + self.nfs_settings_args.update({ + "access_zone": MockNFSZoneSettingsApi.ZONE, + "nfsv4_allow_numeric_ids": False, + "nfsv4_domain": MockNFSZoneSettingsApi.NFS_DOMAIN, + "nfsv4_no_domain": False, + "nfsv4_no_domain_uids": False, + "nfsv4_no_names": False, + "nfsv4_replace_domain": False + }) + nfs_settings_module_mock.module.params = self.nfs_settings_args + nfs_settings_module_mock.protocol_api.get_nfs_settings_zone.to_dict = MagicMock( + return_value=MockNFSZoneSettingsApi.GET_NFS_ZONE_SETTINGS_RESPONSE) + nfs_settings_module_mock.protocol_api.update_nfs_settings_zone = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockNFSZoneSettingsApi.zone_settings_exception( + response_type="update_zone_settings_exception"), + nfs_settings_module_mock) + + def test_prepare_zone_settings_modify_object_exception(self, nfs_settings_module_mock): + self.nfs_settings_args.update({ + "access_zone": MockNFSZoneSettingsApi.ZONE, + "nfsv4_allow_numeric_ids": False, + "nfsv4_domain": MockNFSZoneSettingsApi.NFS_DOMAIN, + "nfsv4_no_domain": False, + "nfsv4_no_domain_uids": False, + "nfsv4_no_names": False, + "nfsv4_replace_domain": False + }) + nfs_settings_module_mock.module.params = self.nfs_settings_args + nfs_settings_module_mock.protocol_api.get_nfs_settings_zone.to_dict = MagicMock( + return_value=MockNFSZoneSettingsApi.GET_NFS_ZONE_SETTINGS_RESPONSE) + nfs_settings_module_mock.isi_sdk.NfsSettingsZoneSettings = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockNFSZoneSettingsApi.zone_settings_exception( + response_type="prepare_zone_settings_object_exception"), + nfs_settings_module_mock) + + def test_get_nfs_zone_settings_invalid_az_exception(self, nfs_settings_module_mock): + self.nfs_settings_args.update({ + "access_zone": "invalid zone", + }) + nfs_settings_module_mock.module.params = self.nfs_settings_args + self.capture_fail_json_call( + MockNFSZoneSettingsApi.zone_settings_exception( + response_type="invalid_zone_exception"), + nfs_settings_module_mock) diff --git a/tests/unit/plugins/modules/test_nfsdefaultsettings.py b/tests/unit/plugins/modules/test_nfsdefaultsettings.py new file mode 100644 index 00000000..0dae7857 --- /dev/null +++ b/tests/unit/plugins/modules/test_nfsdefaultsettings.py @@ -0,0 +1,169 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for user mapping rules module on PowerScale""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +from mock.mock import MagicMock +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell \ + import utils + +utils.get_logger = MagicMock() +utils.isi_sdk = MagicMock() +PREREQS_VALIDATE = { + "all_packages_found": True +} +utils.validate_module_pre_reqs = MagicMock(return_value=PREREQS_VALIDATE) +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() + +from ansible_collections.dellemc.powerscale.plugins.modules.nfs_default_settings import NFSDefaultSettings +from ansible_collections.dellemc.powerscale.plugins.modules.nfs_default_settings import NFSDefaultSettingsHandler +from ansible_collections.dellemc.powerscale.plugins.module_utils.storage.dell.shared_library.protocol \ + import Protocol +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_nfsdefaultsettings_api \ + import MockNfsDefaultSettingsApi +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerscale.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json + + +class TestNfsDefaultSettings(): + nfsdefaultsettings_args = MockNfsDefaultSettingsApi.NFS_DEFAULT_SETTINGS_COMMON_ARGS + + @pytest.fixture + def nfsdefaultsettings_module_mock(self, mocker): + mocker.patch(MockNfsDefaultSettingsApi.MODULE_UTILS_PATH + '.ApiException', new=MockApiException) + nfsdefaultsettings_module_mock = NFSDefaultSettings() + nfsdefaultsettings_module_mock.module.check_mode = False + nfsdefaultsettings_module_mock.module.fail_json = fail_json + return nfsdefaultsettings_module_mock + + def test_get_nfsdefaultsettings(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is False + + def capture_fail_json_call(self, error_msg, nfsdefaultsettings_module_mock): + try: + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + except FailJsonException as fj_object: + assert error_msg == fj_object.message + + def test_update_nfsdefaultsettings_for_map_dict(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + self.nfsdefaultsettings_args.update({ + 'map_root': { + "enabled": True, + "primary_group": "test_user", + "secondary_groups": [ + { + "name": "test_group", + "state": "present" + } + ], + "user": "test_user_2" + } + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is True + + def test_update_nfsdefaultsettings_for_map_dict_two(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + nfsdefaultsettings_details['map_root']['enabled'] = True + self.nfsdefaultsettings_args.update({ + 'map_root': { + "enabled": False, + "secondary_groups": [ + { + "name": "test_group", + "state": "absent" + } + ], + }, + 'file_name_max_size': { + "size_value": 1000, + "size_unit": "KB" + } + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is True + + def test_update_nfsdefaultsettings_for_size_dict(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + self.nfsdefaultsettings_args.update({ + "max_file_size": { + "size_value": 1000, + "size_unit": "KB" + } + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is True + + def test_update_nfsdefaultsettings_for_time_dict(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + self.nfsdefaultsettings_args.update({ + "time_delta": { + "time_value": 1000, + "time_unit": "seconds" + } + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is True + + def test_update_nfsdefaultsettings_for_security_dict(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + self.nfsdefaultsettings_args.update({ + "security_flavors": [ + 'kerberos_integrity' + ] + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is True + + def test_update_nfsdefaultsettings_for_bool_dict(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + self.nfsdefaultsettings_args.update({ + 'commit_asynchronous': True + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + NFSDefaultSettingsHandler().handle(nfsdefaultsettings_module_mock) + assert nfsdefaultsettings_module_mock.module.exit_json.call_args[1]['changed'] is True + + def test_update_nfsdefaultsettings_for_bool_dict_exception(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + self.nfsdefaultsettings_args.update({ + 'commit_asynchronous': True + }) + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + nfsdefaultsettings_module_mock.protocol_api.update_nfs_settings_export = MagicMock(side_effect=MockApiException) + self.capture_fail_json_call(MockNfsDefaultSettingsApi.get_nfsdefaultsettings_exception_response('update_exception'), + nfsdefaultsettings_module_mock) + + def test_update_nfsdefaultsettings_form_modify_exception(self, nfsdefaultsettings_module_mock): + nfsdefaultsettings_details = MockNfsDefaultSettingsApi.GET_NFSDEFAULTSETTINGS_RESPONSE + nfsdefaultsettings_module_mock.module.params = self.nfsdefaultsettings_args + Protocol.get_nfs_default_settings = MagicMock(return_value=nfsdefaultsettings_details) + nfsdefaultsettings_module_mock.form_map_dict = MagicMock(side_effect=MockApiException) + self.capture_fail_json_call(MockNfsDefaultSettingsApi.get_nfsdefaultsettings_exception_response('form_dict_exception'), + nfsdefaultsettings_module_mock)