-
Notifications
You must be signed in to change notification settings - Fork 23.7k
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
Add options sub spec validation #27119
Conversation
nice, had 1/2 of this in patch, but have not had time to finish it, quick review looks good the issue about implementation is that we had already agreed to use a |
Looking at the current So aggregate in the network module spec will look like:
|
yes to move options, no on having alternate implementation named 'aggregate', 'options' should be able to handle the requirements for network also |
bcoca: Added options validation and reverted aggregate implementation. |
ready_for_review |
cc @nitzmahone as he was also starting to develop this feature |
lib/ansible/module_utils/basic.py
Outdated
@@ -773,7 +773,8 @@ class AnsibleModule(object): | |||
def __init__(self, argument_spec, bypass_checks=False, no_log=False, | |||
check_invalid_arguments=True, mutually_exclusive=None, required_together=None, | |||
required_one_of=None, add_file_common_args=False, supports_check_mode=False, | |||
required_if=None): | |||
required_if=None, bypass_options_checks=False, options_mutually_exclusive=None, | |||
options_required_together=None, options_required_one_of=None, options_required_if=None): |
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.
this seems to assume a single level of sub specs, in some cases we might want 2 or 3 so making it recursive with the top options seems like the best approach
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.
For multi level sub specs if some of the args are same across different level it might result in a false error, that's why avoided having a recursive call.
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.
making it recursive with the top options.
Does this mean required_*
conditions or options_required_*
?
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.
bcoca: Fixed assuming module's top level options are applicable for multi-level sub args specs.
|
||
return aliases_results | ||
|
||
def _check_arguments(self, check_invalid_arguments): | ||
def _handle_no_log_values(self, spec=None, param=None): |
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.
does not resolve inheritance #25097
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.
Above referenced issue is not seen with this patch applied.
eg: Module arg spec
argument_spec = dict(
name=dict(),
description=dict(no_log=True),
aggregate=dict(type='list', elements='dict'),
purge=dict(default=False, type='bool'),
state=dict(default='present',
choices=['present', 'absent', 'up', 'down']),
)
aggregate_arg_spec = deepcopy(argument_spec)
del aggregate_arg_spec['aggregate']
argument_spec['aggregate']['options'] = aggregate_arg_spec
Module run log
"aggregate": [
{
"description": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"name": "ge-0/0/12",
"purge": false,
"state": "present",
},
{
"description": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"name": "ge-0/0/13",
"purge": false,
"state": "present",
},
{
"description": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"name": "ge-0/0/14",
"purge": false,
"state": "present",
}
]
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.
_handle_no_log_values()
is also called from _handle_options()
recursively in case on mullti-level argspec.
So if I understand your comment and referenced issue correctly, this patch does resolve inheritance issue.
aggregate parameter validation will support checking each individual dict to resolve conditions for aliases, no_log, mutually_exclusive, required, type check, values, required_together, required_one_of and required_if conditions in argspec. It will also set default values. eg: tasks: - name: Configure interface attribute with aggregate net_interface: aggregate: - {name: ge-0/0/1, description: test-interface-1, duplex: full, state: present} - {name: ge-0/0/2, description: test-interface-2, active: False} register: response purge: Yes Usage: ``` from ansible.module_utils.network_common import AggregateCollection transform = AggregateCollection(module) param = transform(module.params.get('aggregate')) ``` Aggregate allows supports for `purge` parameter, it will instruct the module to remove resources from remote device that hasn’t been explicitly defined in aggregate. This is not supported by with_* iterators Also, it improves performace as compared to with_* iterator for network device that has seperate candidate and running datastore. For with_* iteration the sequence of operartion is load-config-1 (candidate db) -> commit (running db) -> load_config-2 (candidate db) -> commit (running db) ... With aggregate the sequence of operation is load-config-1 (candidate db) -> load-config-2 (candidate db) -> commit (running db) As commit is executed only once per task for aggregate it has huge perfomance benefit for large configurations.
* Add support for options validation for aliases, no_log, mutually_exclusive, required, type check, value check, required_together, required_one_of and required_if conditions in sub-argspec. * Add unit test for options in argspec. * Reverted aggregate implementaion.
* Multi-level argspec support with module's top most conditionals options.
2c4177f
to
50ab0ac
Compare
The test
The test
|
The test
|
if spec is None or not params[k]: | ||
continue | ||
|
||
self._options_context.append(k) |
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.
@nitzmahone Added ability to print parent context in case of error as discussed.
lgtm, all issues I had have been addressed, I'm probably missing something but if we merge this soon we have plenty of time to debug/fix before release. |
bcoca: So shall I go ahead and merge this PR? |
* Add aggregate parameter validation aggregate parameter validation will support checking each individual dict to resolve conditions for aliases, no_log, mutually_exclusive, required, type check, values, required_together, required_one_of and required_if conditions in argspec. It will also set default values. eg: tasks: - name: Configure interface attribute with aggregate net_interface: aggregate: - {name: ge-0/0/1, description: test-interface-1, duplex: full, state: present} - {name: ge-0/0/2, description: test-interface-2, active: False} register: response purge: Yes Usage: ``` from ansible.module_utils.network_common import AggregateCollection transform = AggregateCollection(module) param = transform(module.params.get('aggregate')) ``` Aggregate allows supports for `purge` parameter, it will instruct the module to remove resources from remote device that hasn’t been explicitly defined in aggregate. This is not supported by with_* iterators Also, it improves performace as compared to with_* iterator for network device that has seperate candidate and running datastore. For with_* iteration the sequence of operartion is load-config-1 (candidate db) -> commit (running db) -> load_config-2 (candidate db) -> commit (running db) ... With aggregate the sequence of operation is load-config-1 (candidate db) -> load-config-2 (candidate db) -> commit (running db) As commit is executed only once per task for aggregate it has huge perfomance benefit for large configurations. * Fix CI issues * Fix review comments * Add support for options validation for aliases, no_log, mutually_exclusive, required, type check, value check, required_together, required_one_of and required_if conditions in sub-argspec. * Add unit test for options in argspec. * Reverted aggregate implementaion. * Minor change * Add multi-level argspec support * Multi-level argspec support with module's top most conditionals options. * Fix unit test failure * Add parent context in errors for sub options * Resolve merge conflict * Fix CI issue
@ganeshrn this broke fallback functionality in the f5_utils file. Was this supposed to happen? |
|
||
self._check_required_together(self.required_together, param) | ||
self._check_required_one_of(self.required_one_of, param) | ||
self._check_required_if(self.required_if, param) |
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.
Upon further reflection, I think these three lines along with check_mutually_exclusive
are erroneous for subspecs. We don't currently have a way to specify required_if/one_of/together
or mutually_exclusive
for subspecs, so in every case but the networking "aggregate" thing, this is severely broken (as you'd be making nonsensical checks in most cases)
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.
Only sane way I could think of to support this properly would be to make those values reserved arg names in the arg_spec (so we could define them at any layer of the dict instead of passing them alongside). @bcoca: any other ideas?
SUMMARY
options
handling will support checking each individualdict (element)
to resolve conditions for
aliases
,no_log
,mutually_exclusive
,required
,type check
,value check
,required_together
,required_one_of
and
required_if
conditions in argspec. It will also set default values.eg :
Usage:
purge
parameter in above exampletasks
will instruct the module to remove resources from the remote device that hasn’t been explicitly defined in aggregate. This is not supported by with_* iteratorsAlso, it improves performance as compared to with_* iterator for network device
that has a separate candidate and running datastore.
For with_* iteration, the sequence of operation is
With aggregate the sequence of operation is
Since
commit
is executed once per task for aggregate it hashuge performance benefit for large configurations.
ISSUE TYPE
COMPONENT NAME
module_utils/basic.py
ANSIBLE VERSION
ADDITIONAL INFORMATION