Skip to content
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

WIP: Define module parameters only once. #1314

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 99 additions & 31 deletions plugins/module_utils/ansible_freeipa_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -1628,7 +1628,8 @@ class EntryFactory:
-------
def validate_entry(module, entry, mydata):
if (something_is_wrong(entry)):
module.fail_json(msg=f"Something wrong with {entry.name}")
module.fail_json(
msg="Something wrong with {0}".format(entry.name)
entry.some_field = mydata
return entry

Expand Down Expand Up @@ -1660,35 +1661,100 @@ def main():

def __init__(
self,
ansible_module,
invalid_params,
multiname,
params,
validate_entry=None,
**user_vars
entry_name,
entry_description,
valid_states=("present", "absent"),
has_member_action=True,
has_multientry_support=True,
validate_entry_function=None,
):
"""Initialize the Entry Factory."""
self.ansible_module = ansible_module
self.invalid_params = set(invalid_params)
self.multiname = multiname
self.params = params
self.entry_name = entry_name
self.params = entry_description
self.convert = {
param: (config or {}).get("convert", [])
for param, config in params.items()
for param, config in entry_description.items()
}
self.validate_entry = validate_entry
self.user_vars = user_vars
self.__entries = self._get_entries()
self.validate_entry = validate_entry_function
self.ansible_module = self._init_ansible_module(
valid_states, has_member_action, has_multientry_support
)

def __iter__(self):
"""Initialize factory iterator."""
return iter(self.__entries)
def _init_ansible_module(
self, valid_states, has_member_action, has_multientry_support,
):
mutually_exclusive = []
required_one_of = []
argument_spec = {
"state": {
"type": "str", "default": valid_states[0],
"choices": valid_states,
}
}
if has_member_action:
argument_spec.update({
"action": {
"type": "str",
"default": self.entry_name,
"choices": ["member", self.entry_name],
}
})
multientry_param = "{0}s".format(self.entry_name)
if has_multientry_support:
mutually_exclusive = ["name", multientry_param]
required_one_of = ["name", multientry_param]
# On Python 2.7 we can use just a singlre '**dict' to
# generate the keyword parameters.
argument_spec.update(self._get_entry_spec())
ansible_module = IPAAnsibleModule(
argument_spec=argument_spec,
mutually_exclusive=[mutually_exclusive],
required_one_of=[required_one_of],
supports_check_mode=True,
)
# pylint: disable=attribute-defined-outside-init
ansible_module._ansible_debug = True
return ansible_module

def __next__(self):
"""Retrieve next entry."""
return next(self.__entries)
def _get_entry_spec(self):
"""
Return the entry parameters for AnsibleModule.

The method assumes that the 'name' parameter exists and a list of
elements of its type is accepted is the single entry use case.
"""
name = self.params['name']
name_type = name['type']
if not isinstance(name_type, str):
name_type = name_type.__name__
name_aliases = name.get("aliases", None)
name_param = name.copy()
name_param.update({
"type": 'list',
"elements": name_type,
"required": False,
"default": None,
})
if name_aliases:
name_param["aliases"] = name_aliases
multientry_param = "{0}s".format(self.entry_name)
single_entry = {k: v for k, v in self.params.items() if k != 'name'}
_result = {
"name": name_param,
multientry_param: {
"type": 'list',
"elements": "dict",
"required": False,
"default": None,
"options": self.params,
}
}
_result.update(single_entry)
return _result

def check_invalid_parameter_usage(self, entry_dict, fail_on_check=True):
def check_invalid_parameter_usage(
self, entry_dict, invalid_params, fail_on_check=True
):
"""
Check if entry_dict parameters are valid for the current state/action.

Expand All @@ -1714,7 +1780,7 @@ def check_invalid_parameter_usage(self, entry_dict, fail_on_check=True):
"'{2}' and state '{1}'"

entry_params = set(k for k, v in entry_dict.items() if v is not None)
match_invalid = self.invalid_params & entry_params
match_invalid = invalid_params & entry_params
if match_invalid:
if fail_on_check:
self.ansible_module.fail_json(
Expand Down Expand Up @@ -1759,7 +1825,7 @@ def __repr__(self):
"""Provide a string representation of the stored values."""
return repr(self.values)

def _get_entries(self):
def get_entries(self, invalid_params):
"""Retrieve all entries from the module."""
def copy_entry_and_set_name(entry, name):
_result = entry.copy()
Expand All @@ -1776,7 +1842,8 @@ def copy_entry_and_set_name(entry, name):
# list of names.
_entry = self._extract_entry(
self.ansible_module,
IPAAnsibleModule.params_get
IPAAnsibleModule.params_get,
set(invalid_params),
)
# copy attribute values if 'name' returns a list
_entries = [
Expand All @@ -1785,13 +1852,15 @@ def copy_entry_and_set_name(entry, name):
]
else:
_entries = [
self._extract_entry(data, dict.get)
for data in self.ansible_module.params_get(self.multiname)
self._extract_entry(data, dict.get, set(invalid_params))
for data in self.ansible_module.params_get(
"{0}s".format(self.entry_name)
)
]

return _entries

def _extract_entry(self, data, param_get):
def _extract_entry(self, data, param_get, invalid_params):
"""Extract an entry from the given data, using the given method."""
def get_entry_param_value(parameter, conversions):
_value = param_get(data, parameter)
Expand All @@ -1807,13 +1876,12 @@ def get_entry_param_value(parameter, conversions):
}

# Check if any invalid parameter is used.
self.check_invalid_parameter_usage(_entry)
self.check_invalid_parameter_usage(_entry, invalid_params)

# Create Entry object
_result = EntryFactory.Entry(_entry)
# Call entry validation callback, if provided.
if self.validate_entry:
_result = self.validate_entry(
self.ansible_module, _result, **self.user_vars)
_result = self.validate_entry(self.ansible_module, _result)

return _result
Loading