diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 691ec4f616a..b469a95c9a6 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -1929,23 +1929,14 @@ class AnsibleModule(object): self._options_context.append(k) - key = None - for (name, value) in spec.items(): - # specifies how to map a single value to spec - if value.get('key'): - key = name - break - if isinstance(params[k], dict): elements = [params[k]] else: elements = params[k] - for index, param in enumerate(elements): + for param in elements: if not isinstance(param, dict): - if key is None: - self.fail_json(msg="options spec require a key argument to map it to a single value '%s'" % param) - elements[index] = param = {key: param} + self.fail_json(msg="value of %s must be of type dict or list of dict" % k) self._set_fallbacks(spec, param) options_aliases = self._handle_aliases(spec, param) diff --git a/lib/ansible/module_utils/network/common/utils.py b/lib/ansible/module_utils/network/common/utils.py index 38d46b683ff..cf3faaf91be 100644 --- a/lib/ansible/module_utils/network/common/utils.py +++ b/lib/ansible/module_utils/network/common/utils.py @@ -65,10 +65,6 @@ def sort_list(val): class Entity(object): """Transforms a dict to with an argument spec - This class has been deprecated as of Ansible 2.5 and will be - removed from the code in future release. - Please use the suboptions in module argument spec instead. - This class will take a dict and apply an Ansible argument spec to the values. The resulting dict will contain all of the keys in the param with appropriate values set. @@ -187,12 +183,7 @@ class Entity(object): class EntityCollection(Entity): - """Extends ```Entity``` to handle a list of dicts - - This class has been deprecated as of Ansible 2.5 and will be - removed from the code in future release. - Please use the suboptions in module argument spec instead. - """ + """Extends ```Entity``` to handle a list of dicts """ def __call__(self, iterable, strict=True): if iterable is None: @@ -207,21 +198,11 @@ class EntityCollection(Entity): # these two are for backwards compatibility and can be removed once all of the # modules that use them are updated class ComplexDict(Entity): - """ - This class has been deprecated as of Ansible 2.5 and will be - removed from the code in future release. - Please use the suboptions in module argument spec instead. - """ def __init__(self, attrs, module, *args, **kwargs): super(ComplexDict, self).__init__(module, attrs, *args, **kwargs) class ComplexList(EntityCollection): - """ - This class has been deprecated as of Ansible 2.5 and will be - removed from the code in future release. - Please use the suboptions in module argument spec instead. - """ def __init__(self, attrs, module, *args, **kwargs): super(ComplexList, self).__init__(module, attrs, *args, **kwargs) diff --git a/lib/ansible/module_utils/network/eos/eos.py b/lib/ansible/module_utils/network/eos/eos.py index 3567721ce9d..a548f139345 100644 --- a/lib/ansible/module_utils/network/eos/eos.py +++ b/lib/ansible/module_utils/network/eos/eos.py @@ -33,8 +33,8 @@ import time from ansible.module_utils._text import to_text, to_native from ansible.module_utils.basic import env_fallback, return_values from ansible.module_utils.connection import exec_command -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.six import iteritems, string_types +from ansible.module_utils.network.common.utils import to_list, ComplexList +from ansible.module_utils.six import iteritems from ansible.module_utils.urls import fetch_url _DEVICE_CONNECTION = None @@ -150,6 +150,7 @@ class Cli: """Run list of commands on remote device and return results """ responses = list() + for cmd in to_list(commands): rc, out, err = self.exec_command(cmd) out = to_text(out, errors='surrogate_then_replace') @@ -428,6 +429,22 @@ def is_eapi(module): return 'eapi' in (transport, provider_transport) +def to_command(module, commands): + if is_eapi(module): + default_output = 'json' + else: + default_output = 'text' + + transform = ComplexList(dict( + command=dict(key=True), + output=dict(default=default_output), + prompt=dict(), + answer=dict() + ), module) + + return transform(to_list(commands)) + + def get_config(module, flags=None): flags = None if flags is None else flags @@ -437,15 +454,7 @@ def get_config(module, flags=None): def run_commands(module, commands): conn = get_connection(module) - if is_eapi(module): - default_output = 'json' - else: - default_output = 'text' - - for index, cmd in enumerate(to_list(commands)): - if isinstance(cmd, string_types): - commands[index] = {'command': cmd, 'output': default_output} - return conn.run_commands(to_list(commands)) + return conn.run_commands(to_command(module, commands)) def load_config(module, config, commit=False, replace=False): diff --git a/lib/ansible/module_utils/network/ios/ios.py b/lib/ansible/module_utils/network/ios/ios.py index e27a62bf2af..85ce640093d 100644 --- a/lib/ansible/module_utils/network/ios/ios.py +++ b/lib/ansible/module_utils/network/ios/ios.py @@ -27,9 +27,8 @@ # from ansible.module_utils._text import to_text from ansible.module_utils.basic import env_fallback, return_values +from ansible.module_utils.network.common.utils import to_list, ComplexList from ansible.module_utils.connection import exec_command -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.six import string_types _DEVICE_CONFIGS = {} @@ -101,12 +100,20 @@ def get_config(module, flags=None): return cfg +def to_commands(module, commands): + spec = { + 'command': dict(key=True), + 'prompt': dict(), + 'answer': dict() + } + transform = ComplexList(spec, module) + return transform(commands) + + def run_commands(module, commands, check_rc=True): responses = list() - for cmd in to_list(commands): - if isinstance(cmd, string_types): - cmd = {'command': cmd} - + commands = to_commands(module, to_list(commands)) + for cmd in commands: cmd = module.jsonify(cmd) rc, out, err = exec_command(module, cmd) if check_rc and rc != 0: diff --git a/lib/ansible/module_utils/network/iosxr/iosxr.py b/lib/ansible/module_utils/network/iosxr/iosxr.py index 87b72ceffe5..805acf85129 100644 --- a/lib/ansible/module_utils/network/iosxr/iosxr.py +++ b/lib/ansible/module_utils/network/iosxr/iosxr.py @@ -79,6 +79,12 @@ iosxr_argument_spec = { 'provider': dict(type='dict', options=iosxr_provider_spec) } +command_spec = { + 'command': dict(), + 'prompt': dict(default=None), + 'answer': dict(default=None) +} + iosxr_top_spec = { 'host': dict(removed_in_version=2.9), 'port': dict(removed_in_version=2.9, type='int'), diff --git a/lib/ansible/module_utils/network/nxos/nxos.py b/lib/ansible/module_utils/network/nxos/nxos.py index f6dfd9381e8..4eaad230cda 100644 --- a/lib/ansible/module_utils/network/nxos/nxos.py +++ b/lib/ansible/module_utils/network/nxos/nxos.py @@ -32,7 +32,7 @@ import collections from ansible.module_utils._text import to_text from ansible.module_utils.basic import env_fallback, return_values -from ansible.module_utils.network.common.utils import to_list +from ansible.module_utils.network.common.utils import to_list, ComplexList from ansible.module_utils.connection import exec_command from ansible.module_utils.six import iteritems, string_types from ansible.module_utils.urls import fetch_url @@ -399,6 +399,28 @@ def is_nxapi(module): return 'nxapi' in (transport, provider_transport) +def to_command(module, commands): + if is_nxapi(module): + default_output = 'json' + else: + default_output = 'text' + + transform = ComplexList(dict( + command=dict(key=True), + output=dict(default=default_output), + prompt=dict(), + answer=dict() + ), module) + + commands = transform(to_list(commands)) + + for item in commands: + if is_json(item['command']): + item['output'] = 'json' + + return commands + + def get_config(module, flags=None): flags = [] if flags is None else flags @@ -408,20 +430,7 @@ def get_config(module, flags=None): def run_commands(module, commands, check_rc=True): conn = get_connection(module) - - if is_nxapi(module): - default_output = 'json' - else: - default_output = 'text' - - for index, cmd in enumerate(to_list(commands)): - if isinstance(cmd, string_types): - commands[index] = cmd = {'command': cmd, 'output': default_output} - - if is_json(cmd['command']): - cmd['output'] = 'json' - - return conn.run_commands(to_list(commands), check_rc) + return conn.run_commands(to_command(module, commands), check_rc) def load_config(module, config, return_error=False, opts=None): diff --git a/lib/ansible/modules/network/eos/eos_command.py b/lib/ansible/modules/network/eos/eos_command.py index 8de1451c8c2..7d4c4cd18b2 100644 --- a/lib/ansible/modules/network/eos/eos_command.py +++ b/lib/ansible/modules/network/eos/eos_command.py @@ -140,8 +140,9 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.pycompat24 import get_exception from ansible.module_utils.six import string_types from ansible.module_utils.network.common.parsing import Conditional +from ansible.module_utils.network.common.utils import ComplexList from ansible.module_utils.network.eos.eos import run_commands -from ansible.module_utils.network.eos.eos import eos_argument_spec +from ansible.module_utils.network.eos.eos import eos_argument_spec, check_args VALID_KEYS = ['command', 'output', 'prompt', 'response'] @@ -156,7 +157,16 @@ def to_lines(stdout): def parse_commands(module, warnings): - commands = module.params['commands'] + spec = dict( + command=dict(key=True), + output=dict(), + prompt=dict(), + answer=dict() + ) + + transform = ComplexList(spec, module) + commands = transform(module.params['commands']) + if module.check_mode: for item in list(commands): if not item['command'].startswith('show'): @@ -179,15 +189,8 @@ def to_cli(obj): def main(): """entry point for module execution """ - command_spec = dict( - command=dict(key=True), - output=dict(), - prompt=dict(), - answer=dict() - ) - argument_spec = dict( - commands=dict(type='list', elements='dict', options=command_spec, required=True), + commands=dict(type='list', required=True), wait_for=dict(type='list', aliases=['waitfor']), match=dict(default='all', choices=['all', 'any']), @@ -204,6 +207,7 @@ def main(): result = {'changed': False} warnings = list() + check_args(module, warnings) commands = parse_commands(module, warnings) if warnings: result['warnings'] = warnings diff --git a/lib/ansible/modules/network/eos/eos_system.py b/lib/ansible/modules/network/eos/eos_system.py index 3b525d57402..3ab319f5e70 100644 --- a/lib/ansible/modules/network/eos/eos_system.py +++ b/lib/ansible/modules/network/eos/eos_system.py @@ -129,6 +129,7 @@ session_name: import re from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.common.utils import ComplexList from ansible.module_utils.network.eos.eos import load_config, get_config from ansible.module_utils.network.eos.eos import eos_argument_spec @@ -269,27 +270,31 @@ def map_params_to_obj(module): obj = { 'hostname': module.params['hostname'], 'domain_name': module.params['domain_name'], - 'domain_list': module.params['domain_list'], - 'lookup_source': module.params['lookup_source'], - 'name_servers': module.params['name_servers'], + 'domain_list': module.params['domain_list'] } + lookup_source = ComplexList(dict( + interface=dict(key=True), + vrf=dict() + ), module) + + name_servers = ComplexList(dict( + server=dict(key=True), + vrf=dict(default='default') + ), module) + + for arg, cast in [('lookup_source', lookup_source), ('name_servers', name_servers)]: + if module.params[arg] is not None: + obj[arg] = cast(module.params[arg]) + else: + obj[arg] = None + return obj def main(): """ main entry point for module execution """ - lookup_source_spec = dict( - interface=dict(key=True), - vrf=dict() - ) - - name_servers_spec = dict( - server=dict(key=True), - vrf=dict() - ) - argument_spec = dict( hostname=dict(), @@ -297,10 +302,10 @@ def main(): domain_list=dict(type='list', aliases=['domain_search']), # { interface: , vrf: } - lookup_source=dict(type='list', elements='dict', options=lookup_source_spec), + lookup_source=dict(type='list'), # { server: ; vrf: } - name_servers=dict(type='list', elements='dict', options=name_servers_spec), + name_servers=dict(type='list'), state=dict(default='present', choices=['present', 'absent']) ) @@ -328,6 +333,5 @@ def main(): module.exit_json(**result) - if __name__ == '__main__': main() diff --git a/lib/ansible/modules/network/ios/ios_command.py b/lib/ansible/modules/network/ios/ios_command.py index 7e6d0b31eae..a5c79d21941 100644 --- a/lib/ansible/modules/network/ios/ios_command.py +++ b/lib/ansible/modules/network/ios/ios_command.py @@ -134,8 +134,9 @@ failed_conditions: import time from ansible.module_utils.network.ios.ios import run_commands -from ansible.module_utils.network.ios.ios import ios_argument_spec +from ansible.module_utils.network.ios.ios import ios_argument_spec, check_args from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.common.utils import ComplexList from ansible.module_utils.network.common.parsing import Conditional from ansible.module_utils.six import string_types @@ -148,8 +149,13 @@ def to_lines(stdout): def parse_commands(module, warnings): - commands = module.params['commands'] - for item in commands: + command = ComplexList(dict( + command=dict(key=True), + prompt=dict(), + answer=dict() + ), module) + commands = command(module.params['commands']) + for item in list(commands): if module.check_mode and not item['command'].startswith('show'): warnings.append( 'only show commands are supported when using check mode, not ' @@ -167,14 +173,8 @@ def parse_commands(module, warnings): def main(): """main entry point for module execution """ - command_spec = dict( - command=dict(key=True), - prompt=dict(), - answer=dict() - ) - argument_spec = dict( - commands=dict(type='list', elements='dict', options=command_spec, required=True), + commands=dict(type='list', required=True), wait_for=dict(type='list', aliases=['waitfor']), match=dict(default='all', choices=['all', 'any']), @@ -191,6 +191,7 @@ def main(): result = {'changed': False} warnings = list() + check_args(module, warnings) commands = parse_commands(module, warnings) result['warnings'] = warnings diff --git a/lib/ansible/modules/network/ios/ios_system.py b/lib/ansible/modules/network/ios/ios_system.py index 90f4d2750fa..7b59e14859e 100644 --- a/lib/ansible/modules/network/ios/ios_system.py +++ b/lib/ansible/modules/network/ios/ios_system.py @@ -120,7 +120,8 @@ import re from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.network.ios.ios import get_config, load_config -from ansible.module_utils.network.ios.ios import ios_argument_spec +from ansible.module_utils.network.ios.ios import ios_argument_spec, check_args +from ansible.module_utils.network.common.utils import ComplexList _CONFIGURED_VRFS = None @@ -306,43 +307,44 @@ def map_params_to_obj(module): 'hostname': module.params['hostname'], 'lookup_source': module.params['lookup_source'], 'lookup_enabled': module.params['lookup_enabled'], - 'domain_name': module.params['domain_name'], - 'domain_search': module.params['domain_search'], - 'name_servers': module.params['name_servers'] } + domain_name = ComplexList(dict( + name=dict(key=True), + vrf=dict() + ), module) + + domain_search = ComplexList(dict( + name=dict(key=True), + vrf=dict() + ), module) + + name_servers = ComplexList(dict( + server=dict(key=True), + vrf=dict() + ), module) + + for arg, cast in [('domain_name', domain_name), + ('domain_search', domain_search), + ('name_servers', name_servers)]: + + if module.params[arg]: + obj[arg] = cast(module.params[arg]) + else: + obj[arg] = None + return obj def main(): """ Main entry point for Ansible module execution """ - domain_name_spec = dict( - name=dict(key=True), - vrf=dict() - ) - - domain_search_spec = dict( - name=dict(key=True), - vrf=dict() - ) - - name_servers_spec = dict( - server=dict(key=True), - vrf=dict() - ) - argument_spec = dict( hostname=dict(), - # { name: , vrf: } - domain_name=dict(type='list', elements='dict', options=domain_name_spec), - - # {name: , vrf: } - domain_search=dict(type='list', elements='dict', options=domain_search_spec), - - # { server: ; vrf: } - name_servers=dict(type='list', elements='dict', options=name_servers_spec), + domain_name=dict(type='list'), + domain_search=dict(type='list'), + name_servers=dict(type='list'), lookup_source=dict(), lookup_enabled=dict(type='bool'), @@ -358,6 +360,7 @@ def main(): result = {'changed': False} warnings = list() + check_args(module, warnings) result['warnings'] = warnings want = map_params_to_obj(module) @@ -373,6 +376,5 @@ def main(): module.exit_json(**result) - if __name__ == "__main__": main() diff --git a/lib/ansible/modules/network/iosxr/iosxr_command.py b/lib/ansible/modules/network/iosxr/iosxr_command.py index bd05475d02e..5fca0fdb262 100644 --- a/lib/ansible/modules/network/iosxr/iosxr_command.py +++ b/lib/ansible/modules/network/iosxr/iosxr_command.py @@ -127,8 +127,8 @@ import time from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.network.iosxr.iosxr import run_command, iosxr_argument_spec +from ansible.module_utils.network.iosxr.iosxr import command_spec from ansible.module_utils.network.common.parsing import Conditional -from ansible.module_utils.network.common.utils import to_list from ansible.module_utils.six import string_types from ansible.module_utils._text import to_native @@ -142,8 +142,11 @@ def to_lines(stdout): def parse_commands(module, warnings): commands = module.params['commands'] - for item in to_list(commands): - command = item['command'] + for item in list(commands): + try: + command = item['command'] + except Exception: + command = item if module.check_mode and not command.startswith('show'): warnings.append( 'only show commands are supported when using check mode, not ' @@ -160,14 +163,8 @@ def parse_commands(module, warnings): def main(): - command_spec = dict( - command=dict(key=True), - prompt=dict(), - answer=dict() - ) - spec = dict( - commands=dict(type='list', elements='dict', options=command_spec, required=True), + commands=dict(type='list', required=True), wait_for=dict(type='list', aliases=['waitfor']), match=dict(default='all', choices=['all', 'any']), @@ -178,10 +175,13 @@ def main(): spec.update(iosxr_argument_spec) + spec.update(command_spec) + module = AnsibleModule(argument_spec=spec, supports_check_mode=True) warnings = list() + commands = parse_commands(module, warnings) wait_for = module.params['wait_for'] or list() diff --git a/lib/ansible/modules/network/nxos/nxos_command.py b/lib/ansible/modules/network/nxos/nxos_command.py index 5da430ac3c5..8ebc0e9578f 100644 --- a/lib/ansible/modules/network/nxos/nxos_command.py +++ b/lib/ansible/modules/network/nxos/nxos_command.py @@ -147,7 +147,8 @@ import time from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.network.common.parsing import Conditional, FailedConditionalError -from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, run_commands +from ansible.module_utils.network.common.utils import ComplexList +from ansible.module_utils.network.nxos.nxos import check_args, nxos_argument_spec, run_commands from ansible.module_utils.six import string_types from ansible.module_utils._text import to_native @@ -162,7 +163,14 @@ def to_lines(stdout): def parse_commands(module, warnings): - commands = module.params['commands'] + transform = ComplexList(dict( + command=dict(key=True), + output=dict(), + prompt=dict(), + answer=dict() + ), module) + + commands = transform(module.params['commands']) if module.check_mode: for item in list(commands): @@ -186,16 +194,9 @@ def to_cli(obj): def main(): """entry point for module execution """ - command_spec = dict( - command=dict(key=True), - output=dict(), - prompt=dict(), - answer=dict() - ) - argument_spec = dict( # { command: , output: , prompt: , response: } - commands=dict(type='list', elements='dict', options=command_spec, required=True), + commands=dict(type='list', required=True), wait_for=dict(type='list', aliases=['waitfor']), match=dict(default='all', choices=['any', 'all']), @@ -212,6 +213,7 @@ def main(): result = {'changed': False} warnings = list() + check_args(module, warnings) commands = parse_commands(module, warnings) result['warnings'] = warnings diff --git a/lib/ansible/modules/network/nxos/nxos_system.py b/lib/ansible/modules/network/nxos/nxos_system.py index e616db75863..ac236ee355e 100644 --- a/lib/ansible/modules/network/nxos/nxos_system.py +++ b/lib/ansible/modules/network/nxos/nxos_system.py @@ -112,10 +112,11 @@ commands: import re from ansible.module_utils.network.nxos.nxos import get_config, load_config -from ansible.module_utils.network.nxos.nxos import nxos_argument_spec +from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six import iteritems from ansible.module_utils.network.common.config import NetworkConfig +from ansible.module_utils.network.common.utils import ComplexList _CONFIGURED_VRFS = None @@ -301,45 +302,49 @@ def map_params_to_obj(module): obj = { 'hostname': module.params['hostname'], 'domain_lookup': module.params['domain_lookup'], - 'system_mtu': module.params['system_mtu'], - 'domain_name': module.params['domain_name'], - 'domain_search': module.params['domain_search'], - 'name_servers': module.params['name_servers'] + 'system_mtu': module.params['system_mtu'] } + domain_name = ComplexList(dict( + name=dict(key=True), + vrf=dict() + ), module) + + domain_search = ComplexList(dict( + name=dict(key=True), + vrf=dict() + ), module) + + name_servers = ComplexList(dict( + server=dict(key=True), + vrf=dict() + ), module) + + for arg, cast in [('domain_name', domain_name), ('domain_search', domain_search), + ('name_servers', name_servers)]: + if module.params[arg] is not None: + obj[arg] = cast(module.params[arg]) + else: + obj[arg] = None + return obj def main(): """ main entry point for module execution """ - domain_name_spec = dict( - name=dict(key=True), - vrf=dict() - ) - - domain_search_spec = dict( - name=dict(key=True), - vrf=dict() - ) - - name_servers_spec = dict( - server=dict(key=True), - vrf=dict() - ) - argument_spec = dict( hostname=dict(), domain_lookup=dict(type='bool'), # { name: , vrf: } - domain_name=dict(type='list', elements='dict', options=domain_name_spec), + domain_name=dict(type='list'), # {name: , vrf: } - domain_search=dict(type='list', elements='dict', options=domain_search_spec), + domain_search=dict(type='list'), # { server: ; vrf: } - name_servers=dict(type='list', elements='dict', options=name_servers_spec), + name_servers=dict(type='list'), system_mtu=dict(type='int'), lookup_source=dict(), @@ -352,6 +357,7 @@ def main(): supports_check_mode=True) warnings = list() + check_args(module, warnings) result = {'changed': False} if warnings: @@ -370,6 +376,5 @@ def main(): module.exit_json(**result) - if __name__ == '__main__': main() diff --git a/lib/ansible/modules/network/vyos/vyos_command.py b/lib/ansible/modules/network/vyos/vyos_command.py index da3edf45c62..798ec0077a9 100644 --- a/lib/ansible/modules/network/vyos/vyos_command.py +++ b/lib/ansible/modules/network/vyos/vyos_command.py @@ -137,6 +137,7 @@ import time from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.pycompat24 import get_exception from ansible.module_utils.network.common.parsing import Conditional +from ansible.module_utils.network.common.utils import ComplexList from ansible.module_utils.six import string_types from ansible.module_utils.network.vyos.vyos import run_commands from ansible.module_utils.network.vyos.vyos import vyos_argument_spec @@ -150,7 +151,12 @@ def to_lines(stdout): def parse_commands(module, warnings): - commands = module.params['commands'] + command = ComplexList(dict( + command=dict(key=True), + prompt=dict(), + answer=dict(), + ), module) + commands = command(module.params['commands']) items = [] for item in commands: @@ -164,15 +170,8 @@ def parse_commands(module, warnings): def main(): - - command_spec = dict( - command=dict(key=True), - prompt=dict(), - answer=dict() - ) - spec = dict( - commands=dict(type='list', elements='dict', options=command_spec, required=True), + commands=dict(type='list', required=True), wait_for=dict(type='list', aliases=['waitfor']), match=dict(default='all', choices=['all', 'any']), diff --git a/test/units/module_utils/test_basic.py b/test/units/module_utils/test_basic.py index a575bcda4d9..4df155bc084 100644 --- a/test/units/module_utils/test_basic.py +++ b/test/units/module_utils/test_basic.py @@ -575,21 +575,6 @@ class TestModuleUtilsBasic(ModuleTestCase): supports_check_mode=True, ) - # should test ok, handles key argument - key_spec = dict(foo=dict(key=True), bar=dict()) - args = json.dumps(dict(ANSIBLE_MODULE_ARGS={'foobar': ['test-1', 'test-2']})) - with swap_stdin_and_argv(stdin_data=args): - basic._ANSIBLE_ARGS = None - am = basic.AnsibleModule( - argument_spec=dict(foobar=dict(type='list', elements='dict', options=key_spec, required=True)), - no_log=True, - check_invalid_arguments=False, - add_file_common_args=True, - supports_check_mode=True - ) - self.assertEqual(am.params['foobar'][0]['foo'], 'test-1') - self.assertEqual(am.params['foobar'][1]['foo'], 'test-2') - def test_module_utils_basic_ansible_module_type_check(self): from ansible.module_utils import basic