-
Notifications
You must be signed in to change notification settings - Fork 1.7k
nfs_exports_info: new 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
base: main
Are you sure you want to change the base?
nfs_exports_info: new module #10122
Changes from 51 commits
309fe6d
05a326c
e0c4e3c
4aa341a
8949e2b
fef3a83
2c75f3c
e97bc68
096f019
6367ff0
a0fae4c
fc5073e
9c2dbfc
128bb8b
7dd2544
fafc2c0
2c3bdff
5eda6b9
e77b24f
edba62e
14cff08
37bd9a5
acfc096
c80e76f
da148f6
e6b5d52
4004e07
10e12a5
4cc0b77
1d65305
9944b60
1d57e66
09c3e35
7f328ec
04bf2be
faf1a67
db1ac68
bccf55b
4f4c4bd
d448a63
8d35794
ed5e9de
d088911
07c0ac9
bce822a
66ff472
138e65d
4a19530
ce0fcbb
a5bfc7e
1a99b2a
a5a9d42
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,167 @@ | ||||||||||
#!/usr/bin/python | ||||||||||
|
||||||||||
# SPDX-FileCopyrightText: (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 | ||||||||||
|
||||||||||
from __future__ import absolute_import, division, print_function | ||||||||||
__metaclass__ = type | ||||||||||
|
||||||||||
DOCUMENTATION = r""" | ||||||||||
--- | ||||||||||
module: nfs_exports_info | ||||||||||
|
||||||||||
short_description: Extract folders, IPs, and options from C(/etc/exports) | ||||||||||
|
||||||||||
description: | ||||||||||
- This module retrieves and processes the contents of the C(/etc/exports) file from a remote server, | ||||||||||
mapping folders to their corresponding IP addresses and access options. | ||||||||||
|
||||||||||
author: | ||||||||||
- Samaneh Yousefnezhad (@yousefenzhad) | ||||||||||
version_added: "11.1.0" | ||||||||||
felixfontein marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
extends_documentation_fragment: | ||||||||||
- community.general.attributes | ||||||||||
- community.general.attributes.info_module | ||||||||||
|
||||||||||
options: | ||||||||||
output_format: | ||||||||||
description: | ||||||||||
- The format of the returned mapping. | ||||||||||
- If set to C(ips_per_share), output maps shared folders to IPs and options. | ||||||||||
- If set to C(shares_per_ip), output maps IPs to shared folders and options. | ||||||||||
required: true | ||||||||||
type: str | ||||||||||
choices: ['ips_per_share', 'shares_per_ip'] | ||||||||||
""" | ||||||||||
|
||||||||||
EXAMPLES = r""" | ||||||||||
- name: Get IPs and options per shared folder | ||||||||||
community.general.nfs_exports_info: | ||||||||||
output_format: 'ips_per_share' | ||||||||||
felixfontein marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
register: result | ||||||||||
|
||||||||||
- name: Get shared folders and options per IP | ||||||||||
community.general.nfs_exports_info: | ||||||||||
output_format: 'shares_per_ip' | ||||||||||
""" | ||||||||||
|
||||||||||
RETURN = r""" | ||||||||||
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 | ||||||||||
Comment on lines
+51
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I think there should be two different return values, something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yousefenzhad this is still open, and something that really should be changed. Using the same name for two very different things is a bad idea and prevents this to be documented correctly. |
||||||||||
|
||||||||||
file_digest: | ||||||||||
description: | ||||||||||
- A dictionary containing various hash values of the /etc/exports file for integrity verification. | ||||||||||
- Keys are the hash algorithm names (e.g., 'sha256', 'sha1', 'md5'), and values are their corresponding hexadecimal digests. | ||||||||||
|
- A dictionary containing various hash values of the /etc/exports file for integrity verification. | |
- Keys are the hash algorithm names (e.g., 'sha256', 'sha1', 'md5'), and values are their corresponding hexadecimal digests. | |
- A dictionary containing various hash values of the C(/etc/exports) file for integrity verification. | |
- Keys are the hash algorithm names (for example C(sha256), C(sha1), C(md5)), and values are their corresponding hexadecimal digests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I've made the change and pushed it.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# 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 | ||
felixfontein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
from __future__ import absolute_import, division, print_function | ||
__metaclass__ = type | ||
|
||
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import mock_open, patch, MagicMock | ||
|
||
import pytest | ||
import sys | ||
import hashlib | ||
|
||
from ansible_collections.community.general.plugins.modules.nfs_exports_info import get_exports | ||
|
||
|
||
@pytest.fixture | ||
def fake_exports_content(): | ||
return """ | ||
# Sample exports | ||
/srv/nfs1 192.168.1.10(rw,sync) 192.168.1.20(ro,sync) | ||
/srv/nfs2 192.168.1.30(rw,no_root_squash) | ||
""" | ||
|
||
|
||
def calculate_expected_digests(content_string): | ||
content_bytes = content_string.encode('utf-8') | ||
digests = {} | ||
hash_algorithms = ['sha256', 'sha1', 'md5'] | ||
for algo in hash_algorithms: | ||
try: | ||
hasher = hashlib.new(algo) | ||
hasher.update(content_bytes) | ||
digests[algo] = hasher.hexdigest() | ||
except ValueError: | ||
pass | ||
return digests | ||
|
||
|
||
def test_get_exports_ips_per_share(fake_exports_content): | ||
mock_module = MagicMock() | ||
mock_module.file_exists.return_value = True | ||
mock_module.warn.return_value = None | ||
mock_module.fail_json.side_effect = Exception("fail_json called") | ||
patch_target = "builtins.open" if sys.version_info[0] == 3 else "__builtin__.open" | ||
|
||
with patch(patch_target, mock_open(read_data=fake_exports_content.encode('utf-8'))): | ||
result = get_exports(mock_module, "ips_per_share") | ||
|
||
expected_exports_info = { | ||
'/srv/nfs1': [ | ||
{'ip': '192.168.1.10', 'options': ['rw', 'sync']}, | ||
{'ip': '192.168.1.20', 'options': ['ro', 'sync']} | ||
], | ||
'/srv/nfs2': [ | ||
{'ip': '192.168.1.30', 'options': ['rw', 'no_root_squash']} | ||
] | ||
} | ||
|
||
expected_file_digests = calculate_expected_digests(fake_exports_content) | ||
|
||
assert result['exports_info'] == expected_exports_info | ||
assert result['file_digest'] == expected_file_digests | ||
|
||
|
||
def test_get_exports_shares_per_ip(fake_exports_content): | ||
mock_module = MagicMock() | ||
mock_module.file_exists.return_value = True | ||
mock_module.warn.return_value = None | ||
mock_module.fail_json.side_effect = Exception("fail_json called") | ||
patch_target = "builtins.open" if sys.version_info[0] == 3 else "__builtin__.open" | ||
|
||
with patch(patch_target, mock_open(read_data=fake_exports_content.encode('utf-8'))): | ||
result = get_exports(mock_module, "shares_per_ip") | ||
|
||
expected_exports_info = { | ||
'192.168.1.10': [ | ||
{'folder': '/srv/nfs1', 'options': ['rw', 'sync']} | ||
], | ||
'192.168.1.20': [ | ||
{'folder': '/srv/nfs1', 'options': ['ro', 'sync']} | ||
], | ||
'192.168.1.30': [ | ||
{'folder': '/srv/nfs2', 'options': ['rw', 'no_root_squash']} | ||
] | ||
} | ||
|
||
expected_file_digests = calculate_expected_digests(fake_exports_content) | ||
|
||
assert result['exports_info'] == expected_exports_info | ||
assert result['file_digest'] == expected_file_digests |
Uh oh!
There was an error while loading. Please reload this page.