Skip to content

initial nfs_exports_info module #10122

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

yousefenzhad
Copy link

SUMMARY

This pull request introduces a new Ansible module that parses the /etc/exports file
to extract NFS shared folder information, including export paths and associated options.
This is useful for automation tasks that require visibility into NFS export configurations.

ISSUE TYPE
  • New Module/Plugin Pull Request
COMPONENT NAME

nfs_exports_info

ADDITIONAL INFORMATION

This module reads and parses the /etc/exports file line by line, ignoring comments and blank lines,
and returns a structured list of dictionaries with the following information for each export:

  • path
  • clients and their export options

@ansibullbot ansibullbot added module module new_contributor Help guide this first time contributor new_plugin New plugin plugins plugin (any type) tests tests unit tests/unit labels May 12, 2025
@ansibullbot

This comment was marked as outdated.

@ansibullbot ansibullbot added the needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR label May 12, 2025
@ansibullbot

This comment was marked as outdated.

@felixfontein felixfontein added check-before-release PR will be looked at again shortly before release and merged if possible. backport-10 Automatically create a backport for the stable-10 branch labels May 12, 2025
@ansibullbot

This comment was marked as outdated.

@ansibullbot ansibullbot added the needs_ci This PR requires CI testing to be performed. Please close and re-open this PR to trigger CI label May 13, 2025
@ansibullbot ansibullbot removed the needs_ci This PR requires CI testing to be performed. Please close and re-open this PR to trigger CI label May 13, 2025
@ansibullbot ansibullbot removed the needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR label May 13, 2025
Copy link
Collaborator

@felixfontein felixfontein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution!

Comment on lines 1 to 4
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright: (c) 2025, Samaneh Yousefnezhad <[email protected]>
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright: (c) 2025, Samaneh Yousefnezhad <[email protected]>
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#!/usr/bin/python
# Copyright (c) 2025, Samaneh Yousefnezhad <[email protected]>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

@@ -0,0 +1,74 @@
# SPDX-License-Identifier: GPL-3.0-or-later
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2025, Samaneh Yousefnezhad <[email protected]>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also applies for the module itself

---
module: nfs_exports_info

short_description: Extract folders, IPs, and options from /etc/exports
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
short_description: Extract folders, IPs, and options from /etc/exports
short_description: Extract folders, IPs, and options from C(/etc/exports)
version_added: 10.7.0

EXAMPLES = r"""
- name: Get IPs and options per shared folder
fava.infra.nfs_exports_info:
output_format: 'ips_per_share'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a register to make it more obvious that you should register the result:

Suggested change
output_format: 'ips_per_share'
output_format: 'ips_per_share'
register: result


def get_exports(module, output_format, file_path="/etc/exports"):
try:
exports_file_digest = module.digest_from_file(file_path, 'sha1')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHA1 is not always available. You need to do some fallback, or (better) provide at least a few different hash values based on availability. (I'd make the return value a dictionary with the key being the hash's name.)

mapping folders to their corresponding IP addresses and access options.

author:
- Samaneh Yousefnezhad (@yousefenzhad)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Samaneh Yousefnezhad (@yousefenzhad)
- Samaneh Yousefnezhad (@yousefenzhad)
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module

Comment on lines +14 to +19
# Add plugins/modules to path for direct import
sys.path.insert(0, os.path.abspath(
os.path.join(os.path.dirname(__file__), '../../../../plugins/modules')
))

from nfs_exports_info import get_exports
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Add plugins/modules to path for direct import
sys.path.insert(0, os.path.abspath(
os.path.join(os.path.dirname(__file__), '../../../../plugins/modules')
))
from nfs_exports_info import get_exports
from ansible_collections.community.general.plugins.modules.nfs_exports_info import get_exports

Comment on lines +5 to +8
try:
from unittest.mock import mock_open, patch, MagicMock
except ImportError:
from mock import mock_open, patch, MagicMock
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
try:
from unittest.mock import mock_open, patch, MagicMock
except ImportError:
from mock import mock_open, patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import mock_open, patch, MagicMock

@ansibullbot

This comment was marked as outdated.

@ansibullbot ansibullbot added ci_verified Push fixes to PR branch to re-run CI needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR labels May 17, 2025
@ansibullbot ansibullbot removed ci_verified Push fixes to PR branch to re-run CI needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR labels May 17, 2025
Copy link
Collaborator

@russoz russoz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @yousefenzhad thanks for your contribution!

I left some comments.

Comment on lines +48 to +53
exports_info:
description:
- A mapping of shared folders to IPs and their options, or the reverse.
- What it is depends on O(output_format).
type: dict
returned: always
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I think there should be two different return values, something like exports_per_ip, exports_per_share, and the format should be fixed for each one, instead of having a single return value with a variable format. The info part is redundant, given the module name.

Comment on lines +71 to +76
try:
f = open(file_path, 'r')
output_lines = f.readlines()
f.close()
except IOError:
module.fail_json(msg="Could not read {}".format(file_path))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I may suggest, make this a small, simple function, say read_exports or something similar, and that will simplify mocking/patching in tests/unit/plugins/modules/test_nfs_exports_info.py line 40

Comment on lines +79 to +86
pattern = r'\s*(\S+)\s+(.+)'

for line in output_lines:
line = line.strip()
if not line or line.startswith('#'):
continue

match = re.match(pattern, line)
Copy link
Collaborator

@russoz russoz May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strongly recommend compiling the regexp outside of the loop:

Suggested change
pattern = r'\s*(\S+)\s+(.+)'
for line in output_lines:
line = line.strip()
if not line or line.startswith('#'):
continue
match = re.match(pattern, line)
pattern = re.compile(r'\s*(\S+)\s+(.+)')
for line in output_lines:
line = line.strip()
if not line or line.startswith('#'):
continue
match = pattern.match(line)

folder = match.group(1)
rest = match.group(2)

entries = re.findall(r'(\d+\.\d+\.\d+\.\d+)\(([^)]+)\)', rest)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compile it outside the loop as well, pls.

}

except Exception as e:
module.fail_json(msg="Error while processing exports: {}".format(str(e)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is redundant, format() will already convert it to a string.

Suggested change
module.fail_json(msg="Error while processing exports: {}".format(str(e)))
module.fail_json(msg="Error while processing exports: {}".format(e))

@russoz
Copy link
Collaborator

russoz commented May 20, 2025

@felixfontein I am thinking this might be better off as a facts module, rather than just info. It is some system information, unlikely to change often (for most cases).

@felixfontein
Copy link
Collaborator

@felixfontein I am thinking this might be better off as a facts module, rather than just info. It is some system information, unlikely to change often (for most cases).

That's fine with me. But in that case the output_format option needs to be redone or removed. (And the return values definitely have to be worked in that case. I also suggested that to be done in any case.)

@ansibullbot ansibullbot added needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR and removed needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR labels May 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-10 Automatically create a backport for the stable-10 branch check-before-release PR will be looked at again shortly before release and merged if possible. module module new_contributor Help guide this first time contributor new_plugin New plugin plugins plugin (any type) tests tests unit tests/unit
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants