diff --git a/lib/ansible/module_utils/ios_cli.py b/lib/ansible/module_utils/ios_cli.py index 27e92b5219..16b8ec4ca3 100644 --- a/lib/ansible/module_utils/ios_cli.py +++ b/lib/ansible/module_utils/ios_cli.py @@ -46,15 +46,12 @@ ios_cli_argument_spec = { 'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), 'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True), - 'authorize': dict(default=False, fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']), type='bool'), - 'auth_pass': dict(no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS'])), + 'authorize': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']), type='bool'), + 'auth_pass': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS']), no_log=True), 'timeout': dict(type='int', default=10), 'provider': dict(type='dict'), - - # deprecated in Ansible 2.3 - 'transport': dict(), } def check_args(module): @@ -79,8 +76,6 @@ class Cli(CliBase): re.compile(r"[^\r\n]+ not found", re.I), ] - NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) - def __init__(self, module): self._module = module super(Cli, self).__init__() @@ -106,7 +101,12 @@ class Cli(CliBase): def authorize(self): passwd = self._module.params['auth_pass'] - self.execute(Command('enable', prompt=self.NET_PASSWD_RE, response=passwd)) + if passwd: + prompt = "[\r\n]?Password: $" + self.exec_command(dict(command='enable', prompt=prompt, response=passwd)) + else: + self.exec_command('enable') + def connection(module): diff --git a/lib/ansible/modules/network/ios/_ios_template.py b/lib/ansible/modules/network/ios/_ios_template.py index cb1b246e56..6d7101c799 100644 --- a/lib/ansible/modules/network/ios/_ios_template.py +++ b/lib/ansible/modules/network/ios/_ios_template.py @@ -35,9 +35,7 @@ description: commands that are not already configured. The config source can be a set of commands or a template. deprecated: Deprecated in 2.2. Use M(ios_config) instead. -notes: - - Provider arguments are no longer supported. Network tasks should now - specify connection plugin network_cli instead. +extends_documentation_fragment: ios options: src: description: @@ -126,22 +124,35 @@ delta: type: str sample: "0:00:10.469466" """ +from functools import partial + +from ansible.module_utils import ios +from ansible.module_utils import ios_cli +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.local import LocalAnsibleModule +from ansible.module_utils.network_common import ComplexList +from ansible.module_utils.netcli import Conditional +from ansible.module_utils.six import string_types from ansible.module_utils.netcfg import NetworkConfig, dumps -from ansible.module_utils.ios import get_config, load_config -from ansible.module_utils.network import NET_TRANSPORT_ARGS, _transitional_argument_spec +SHARED_LIB = 'ios' -def check_args(module): - warnings = list() - for key in NET_TRANSPORT_ARGS: - if module.params[key]: - warnings.append( - 'network provider arguments are no longer supported. Please ' - 'use connection: network_cli for the task' - ) - break - return warnings +def get_ansible_module(): + if SHARED_LIB == 'ios': + return LocalAnsibleModule + return AnsibleModule + +def invoke(name, *args, **kwargs): + obj = globals().get(SHARED_LIB) + func = getattr(obj, name) + return func(*args, **kwargs) + +load_config = partial(invoke, 'load_config') +get_config = partial(invoke, 'get_config') + +def check_args(module, warnings): + if SHARED_LIB == 'ios_cli': + ios_cli.check_args(module) def get_current_config(module): if module.params['config']: @@ -163,23 +174,23 @@ def main(): config=dict(), ) - # Removed the use of provider arguments in 2.3 due to network_cli - # connection plugin. To be removed in 2.5 - argument_spec.update(_transitional_argument_spec()) + argument_spec.update(ios_cli.ios_cli_argument_spec) mutually_exclusive = [('config', 'backup'), ('config', 'force')] - module = LocalAnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) + cls = get_ansible_module() + module = cls(argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True) - warnings = check_args(module) - - result = dict(changed=False, warnings=warnings) + warnings = list() + check_args(module, warnings) candidate = NetworkConfig(contents=module.params['src'], indent=1) result = {'changed': False} + if warnings: + result['warnings'] = warnings if module.params['backup']: result['__backup__'] = get_config(module=module) @@ -203,4 +214,5 @@ def main(): module.exit_json(**result) if __name__ == '__main__': + SHARED_LIB = 'ios_cli' main() diff --git a/lib/ansible/modules/network/ios/ios_command.py b/lib/ansible/modules/network/ios/ios_command.py index e108e77fc3..3f85db46e3 100644 --- a/lib/ansible/modules/network/ios/ios_command.py +++ b/lib/ansible/modules/network/ios/ios_command.py @@ -35,9 +35,7 @@ description: before returning or timing out if the condition is not met. - This module does not support running commands in configuration mode. Please use M(ios_config) to configure IOS devices. -notes: - - Provider arguments are no longer supported. Network tasks should now - specify connection plugin network_cli instead. +extends_documentation_fragment: ios options: commands: description: @@ -149,13 +147,33 @@ delta: """ import time +from functools import partial + +from ansible.module_utils import ios +from ansible.module_utils import ios_cli +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.local import LocalAnsibleModule -from ansible.module_utils.ios import run_commands from ansible.module_utils.network_common import ComplexList from ansible.module_utils.netcli import Conditional from ansible.module_utils.six import string_types -VALID_KEYS = ['command', 'output'] +SHARED_LIB = 'ios' + +def get_ansible_module(): + if SHARED_LIB == 'ios': + return LocalAnsibleModule + return AnsibleModule + +def invoke(name, *args, **kwargs): + obj = globals().get(SHARED_LIB) + func = getattr(obj, name) + return func(*args, **kwargs) + +run_commands = partial(invoke, 'run_commands') + +def check_args(module, warnings): + if SHARED_LIB == 'ios_cli': + ios_cli.check_args(module) def to_lines(stdout): for item in stdout: @@ -186,7 +204,9 @@ def parse_commands(module, warnings): return commands def main(): - spec = dict( + """main entry point for module execution + """ + argument_spec = dict( # { command: , prompt: , response: } commands=dict(type='list', required=True), @@ -197,10 +217,16 @@ def main(): interval=dict(default=1, type='int') ) - module = LocalAnsibleModule(argument_spec=spec, - supports_check_mode=True) + argument_spec.update(ios_cli.ios_cli_argument_spec) + + cls = get_ansible_module() + module = cls(argument_spec=argument_spec, supports_check_mode=True) warnings = list() + check_args(module, warnings) + + result = {'changed': False} + commands = parse_commands(module, warnings) wait_for = module.params['wait_for'] or list() @@ -243,4 +269,5 @@ def main(): if __name__ == '__main__': + SHARED_LIB = 'ios_cli' main() diff --git a/lib/ansible/modules/network/ios/ios_config.py b/lib/ansible/modules/network/ios/ios_config.py index ad0c5ac19d..ac784d8bd0 100644 --- a/lib/ansible/modules/network/ios/ios_config.py +++ b/lib/ansible/modules/network/ios/ios_config.py @@ -33,9 +33,7 @@ description: for segmenting configuration into sections. This module provides an implementation for working with IOS configuration sections in a deterministic way. -notes: - - Provider arguments are no longer supported. Network tasks should now - specify connection plugin network_cli instead. +extends_documentation_fragment: ios options: lines: description: @@ -223,14 +221,39 @@ delta: import re import time +from functools import partial + +from ansible.module_utils import ios +from ansible.module_utils import ios_cli +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.local import LocalAnsibleModule -from ansible.module_utils.ios import load_config, get_config, run_commands +from ansible.module_utils.network_common import ComplexList +from ansible.module_utils.netcli import Conditional +from ansible.module_utils.six import string_types from ansible.module_utils.netcfg import NetworkConfig, dumps from ansible.module_utils.six import iteritems -from ansible.module_utils.network import NET_TRANSPORT_ARGS, _transitional_argument_spec +SHARED_LIB = 'ios' + +def get_ansible_module(): + if SHARED_LIB == 'ios': + return LocalAnsibleModule + return AnsibleModule + +def invoke(name, *args, **kwargs): + obj = globals().get(SHARED_LIB) + func = getattr(obj, name) + return func(*args, **kwargs) + +run_commands = partial(invoke, 'run_commands') +load_config = partial(invoke, 'load_config') +get_config = partial(invoke, 'get_config') + def check_args(module, warnings): + if SHARED_LIB == 'ios_cli': + ios_cli.check_args(module) + if module.params['multiline_delimiter']: if len(module.params['multiline_delimiter']) != 1: module.fail_json(msg='multiline_delimiter value can only be a ' @@ -240,14 +263,6 @@ def check_args(module, warnings): 'please use match=none instead. This argument will ' 'be removed in the future') - for key in NET_TRANSPORT_ARGS: - if module.params[key]: - warnings.append( - 'network provider arguments are no longer supported. Please ' - 'use connection: network_cli for the task' - ) - break - def extract_banners(config): banners = {} banner_cmds = re.findall(r'^banner (\w+)', config, re.M) @@ -335,7 +350,7 @@ def main(): save=dict(type='bool', default=False), ) - argument_spec.update(_transitional_argument_spec()) + argument_spec.update(ios_cli.ios_cli_argument_spec) mutually_exclusive = [('lines', 'src')] @@ -343,10 +358,14 @@ def main(): ('match', 'exact', ['lines']), ('replace', 'block', ['lines'])] - module = LocalAnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) + cls = get_ansible_module() + module = cls(argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + supports_check_mode=True) + + warnings = list() + check_args(module, warnings) if module.params['force'] is True: module.params['match'] = 'none' @@ -409,4 +428,5 @@ def main(): if __name__ == '__main__': + SHARED_LIB = 'ios_cli' main() diff --git a/lib/ansible/modules/network/ios/ios_facts.py b/lib/ansible/modules/network/ios/ios_facts.py index abe68ec69c..f692d6fa11 100644 --- a/lib/ansible/modules/network/ios/ios_facts.py +++ b/lib/ansible/modules/network/ios/ios_facts.py @@ -15,9 +15,11 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'core', - 'version': '1.0'} +ANSIBLE_METADATA = { + 'status': ['preview'], + 'supported_by': 'core', + 'version': '1.0' +} DOCUMENTATION = """ --- @@ -140,32 +142,58 @@ ansible_net_neighbors: type: dict """ import re -import itertools -import ansible.module_utils.ios -from ansible.module_utils.network import NetworkModule +from functools import partial + +from ansible.module_utils import ios +from ansible.module_utils import ios_cli +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.local import LocalAnsibleModule from ansible.module_utils.six import iteritems from ansible.module_utils.six.moves import zip +SHARED_LIB = 'ios' + +def get_ansible_module(): + if SHARED_LIB == 'ios': + return LocalAnsibleModule + return AnsibleModule + +def invoke(name, *args, **kwargs): + obj = globals().get(SHARED_LIB) + func = getattr(obj, name) + return func(*args, **kwargs) + +run_commands = partial(invoke, 'run_commands') + +def check_args(module, warnings): + if SHARED_LIB == 'ios_cli': + ios_cli.check_args(module) + class FactsBase(object): + COMMANDS = list() + def __init__(self, module): self.module = module self.facts = dict() - self.failed_commands = list() + self.responses = None + + + def populate(self): + self.responses = run_commands(self.module, self.COMMANDS, check_rc=False) def run(self, cmd): - try: - return self.module.cli(cmd)[0] - except: - self.failed_commands.append(cmd) - + return run_commands(self.module, cmd, check_rc=False) class Default(FactsBase): + COMMANDS = ['show version'] + def populate(self): - data = self.run('show version') + super(Default, self).populate() + data = self.responses[0] if data: self.facts['version'] = self.parse_version(data) self.facts['serialnum'] = self.parse_serialnum(data) @@ -201,12 +229,18 @@ class Default(FactsBase): class Hardware(FactsBase): + COMMANDS = [ + 'dir | include Directory', + 'show memory statistics | include Processor' + ] + def populate(self): - data = self.run('dir | include Directory') + super(Hardware, self).populate() + data = self.responses[0] if data: self.facts['filesystems'] = self.parse_filesystems(data) - data = self.run('show memory statistics | include Processor') + data = self.responses[1] if data: match = re.findall(r'\s(\d+)\s', data) if match: @@ -219,33 +253,44 @@ class Hardware(FactsBase): class Config(FactsBase): + COMMANDS = ['show running-config'] + def populate(self): - data = self.run('show running-config') + super(Config, self).populate() + data = self.responses[0] if data: self.facts['config'] = data class Interfaces(FactsBase): + COMMANDS = [ + 'show interfaces', + 'show ipv6 interface', + 'show lldp' + ] + def populate(self): + super(Interfaces, self).populate() + self.facts['all_ipv4_addresses'] = list() self.facts['all_ipv6_addresses'] = list() - data = self.run('show interfaces') + data = self.responses[0] if data: interfaces = self.parse_interfaces(data) self.facts['interfaces'] = self.populate_interfaces(interfaces) - data = self.run('show ipv6 interface') + data = self.responses[1] if data: data = self.parse_interfaces(data) self.populate_ipv6_interfaces(data) - data = self.run('show lldp') - if 'LLDP is not enabled' not in data: + data = self.responses[2] + if data: neighbors = self.run('show lldp neighbors detail') if neighbors: - self.facts['neighbors'] = self.parse_neighbors(neighbors) + self.facts['neighbors'] = self.parse_neighbors(neighbors[0]) def populate_interfaces(self, interfaces): facts = dict() @@ -392,11 +437,19 @@ FACT_SUBSETS = dict( VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) def main(): - spec = dict( + """main entry point for module execution + """ + argument_spec = dict( gather_subset=dict(default=['!config'], type='list') ) - module = NetworkModule(argument_spec=spec, supports_check_mode=True) + argument_spec.update(ios_cli.ios_cli_argument_spec) + + cls = get_ansible_module() + module = cls(argument_spec=argument_spec, supports_check_mode=True) + + warnings = list() + check_args(module, warnings) gather_subset = module.params['gather_subset'] @@ -438,24 +491,18 @@ def main(): for key in runable_subsets: instances.append(FACT_SUBSETS[key](module)) - failed_commands = list() - - try: - for inst in instances: - inst.populate() - failed_commands.extend(inst.failed_commands) - facts.update(inst.facts) - except Exception: - exc = get_exception() - module.fail_json(msg=str(exc)) + for inst in instances: + inst.populate() + facts.update(inst.facts) ansible_facts = dict() for key, value in iteritems(facts): key = 'ansible_net_%s' % key ansible_facts[key] = value - module.exit_json(ansible_facts=ansible_facts, failed_commands=failed_commands) + module.exit_json(ansible_facts=ansible_facts) if __name__ == '__main__': + SHARED_LIB = 'ios_cli' main()