From 972dc3fc97602f8402a995dad006a71cb42402c1 Mon Sep 17 00:00:00 2001 From: Nathaniel Case Date: Tue, 6 Sep 2016 13:49:48 -0400 Subject: [PATCH] Network module cleanup (#17334) * Clean up EOS, IOS, IOS-XR, Junos, NX-OS, and OpenSwitch * Cleanup net* files * Re-add NetworkModule import to network module_utils files This will trick modules into importing code from module_utils code, thus including it in the final Ansiballz zipfile. * Give asa a look over, too --- lib/ansible/module_utils/asa.py | 7 +- lib/ansible/module_utils/eos.py | 43 ++++---- lib/ansible/module_utils/ios.py | 137 ++++++++++++------------- lib/ansible/module_utils/iosxr.py | 18 +--- lib/ansible/module_utils/junos.py | 4 +- lib/ansible/module_utils/netcfg.py | 10 +- lib/ansible/module_utils/netcli.py | 1 - lib/ansible/module_utils/nxos.py | 27 +++-- lib/ansible/module_utils/openswitch.py | 32 +++--- 9 files changed, 128 insertions(+), 151 deletions(-) diff --git a/lib/ansible/module_utils/asa.py b/lib/ansible/module_utils/asa.py index e24f3181e8d..3d37ecfad8d 100644 --- a/lib/ansible/module_utils/asa.py +++ b/lib/ansible/module_utils/asa.py @@ -29,7 +29,7 @@ import re -from ansible.module_utils.network import NetworkModule, NetworkError +from ansible.module_utils.network import NetworkError, NetworkModule from ansible.module_utils.network import add_argument, register_transport, to_list from ansible.module_utils.shell import CliBase from ansible.module_utils.netcli import Command @@ -72,11 +72,6 @@ class Cli(CliBase): cmd = Command('enable', prompt=self.NET_PASSWD_RE, response=passwd) self.execute([cmd, 'no terminal pager']) - ### Cli methods ### - - def run_commands(self, commands): - return self.execute(to_list(commands)) - def change_context(self, params, **kwargs): context = params['context'] if context == 'system': diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py index 1a79b948a89..e0a6eff44a0 100644 --- a/lib/ansible/module_utils/eos.py +++ b/lib/ansible/module_utils/eos.py @@ -28,11 +28,9 @@ import re -from ansible.module_utils.basic import json, get_exception -from ansible.module_utils.network import NetworkModule, NetworkError, ModuleStub +from ansible.module_utils.basic import json +from ansible.module_utils.network import ModuleStub, NetworkError, NetworkModule from ansible.module_utils.network import add_argument, register_transport, to_list -from ansible.module_utils.netcfg import NetworkConfig -from ansible.module_utils.netcli import Command from ansible.module_utils.shell import CliBase from ansible.module_utils.urls import fetch_url, url_argument_spec @@ -41,9 +39,10 @@ EAPI_FORMATS = ['json', 'text'] add_argument('use_ssl', dict(default=True, type='bool')) add_argument('validate_certs', dict(default=True, type='bool')) + class EosConfigMixin(object): - ### implementation of netcfg.Config ### + ### Config methods ### def configure(self, commands, **kwargs): cmds = ['configure terminal'] @@ -93,8 +92,6 @@ class EosConfigMixin(object): def save_config(self): self.execute(['copy running-config startup-config']) - ### end netcfg.Config ### - def diff_config(self, session): commands = ['configure session %s' % session, 'show session-config diffs', @@ -166,24 +163,32 @@ class Eapi(EosConfigMixin): else: self.enable = 'enable' + ### Command methods ### + + def execute(self, commands, output='json', **kwargs): + """Send commands to the device. + """ + if self.url is None: + raise NetworkError('Not connected to endpoint.') - ### implementation of network.Cli ### + if self.enable is not None: + commands.insert(0, self.enable) - def run_commands(self, commands): + def run_commands(self, commands, **kwargs): output = None cmds = list() responses = list() for cmd in commands: if output and output != cmd.output: - responses.extend(self.execute(cmds, format=output)) + responses.extend(self.execute(cmds, output=output)) cmds = list() output = cmd.output cmds.append(str(cmd)) if cmds: - responses.extend(self.execute(cmds, format=output)) + responses.extend(self.execute(cmds, output=output)) for index, cmd in enumerate(commands): if cmd.output == 'text': @@ -191,15 +196,6 @@ class Eapi(EosConfigMixin): return responses - def execute(self, commands, format='json', **kwargs): - """Send commands to the device. - """ - if self.url is None: - raise NetworkError('Not connected to endpoint.') - - if self.enable is not None: - commands.insert(0, self.enable) - data = self._get_body(commands, format) data = json.dumps(data) @@ -230,8 +226,10 @@ class Eapi(EosConfigMixin): return response['result'] + ### Config methods ### + def get_config(self, **kwargs): - return self.execute(['show running-config'], format='text')[0]['output'] + return self.execute(['show running-config'], output='text')[0]['output'] Eapi = register_transport('eapi')(Eapi) @@ -265,7 +263,7 @@ class Cli(EosConfigMixin, CliBase): passwd = params['auth_pass'] self.execute(Command('enable', prompt=self.NET_PASSWD_RE, response=passwd)) - ### implementation of network.Cli ### + ### Command methods ### def run_commands(self, commands): """execute the ordered set of commands on the remote host @@ -300,6 +298,7 @@ def prepare_config(commands): commands.append('end') return commands + def prepare_commands(commands): """ transforms a list of Command objects to dict diff --git a/lib/ansible/module_utils/ios.py b/lib/ansible/module_utils/ios.py index 38ba1dab012..537efe0ef57 100644 --- a/lib/ansible/module_utils/ios.py +++ b/lib/ansible/module_utils/ios.py @@ -18,78 +18,19 @@ # import re -import urlparse from ansible.module_utils.basic import json -from ansible.module_utils.network import NetworkModule, NetworkError, ModuleStub +from ansible.module_utils.netcli import Command +from ansible.module_utils.network import ModuleStub, NetworkError, NetworkModule from ansible.module_utils.network import add_argument, register_transport, to_list from ansible.module_utils.shell import CliBase -from ansible.module_utils.netcli import Command +from ansible.module_utils.six.moves.urllib.parse import urlparse from ansible.module_utils.urls import fetch_url, url_argument_spec add_argument('use_ssl', dict(default=True, type='bool')) add_argument('validate_certs', dict(default=True, type='bool')) -class Cli(CliBase): - - NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) - - CLI_PROMPTS_RE = [ - re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), - re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$") - ] - - CLI_ERRORS_RE = [ - re.compile(r"% ?Error"), - re.compile(r"% ?Bad secret"), - re.compile(r"invalid input", re.I), - re.compile(r"(?:incomplete|ambiguous) command", re.I), - re.compile(r"connection timed out", re.I), - re.compile(r"[^\r\n]+ not found", re.I), - re.compile(r"'[^']' +returned error code: ?\d+"), - ] - - def connect(self, params, **kwargs): - super(Cli, self).connect(params, kickstart=False, **kwargs) - self.shell.send('terminal length 0') - self._connected = True - - def authorize(self, params, **kwargs): - passwd = params['auth_pass'] - cmd = Command('enable', prompt=self.NET_PASSWD_RE, response=passwd) - self.execute([cmd]) - - ### implementation of netcli.Cli ### - - def run_commands(self, commands): - return self.execute(to_list(commands)) - - ### implementation of netcfg.Config ### - - def configure(self, commands): - cmds = ['configure terminal'] - cmds.extend(to_list(commands)) - if cmds[-1] != 'end': - cmds.append('end') - responses = self.execute(cmds) - return responses[1:] - - def get_config(self, include_defaults=False, **kwargs): - cmd = 'show running-config' - if include_defaults: - cmd += ' all' - return self.run_commands(cmd)[0] - - def load_config(self, commands, **kwargs): - return self.configure(commands) - - def save_config(self): - self.execute(['copy running-config startup-config']) - -Cli = register_transport('cli', default=True)(Cli) - - class Restconf(object): DEFAULT_HEADERS = { @@ -99,7 +40,6 @@ class Restconf(object): def __init__(self): self.url = None - self.url_args = ModuleStub(url_argument_spec(), self._error) self.token = None @@ -115,6 +55,7 @@ class Restconf(object): host = params['host'] port = params['port'] or 55443 + # sets the module_utils/urls.py req parameters self.url_args.params['url_username'] = params['username'] self.url_args.params['url_password'] = params['password'] self.url_args.params['validate_certs'] = params['validate_certs'] @@ -127,13 +68,13 @@ class Restconf(object): self.link = response['link'] self._connected = True - def disconnect(self): + def disconnect(self, **kwargs): self.delete(self.link) + self.url = None self._connected = False - def authorize(self): - pass - + def authorize(self, params, **kwargs): + raise NotImplementedError ### REST methods ### @@ -178,10 +119,9 @@ class Restconf(object): def delete(self, path, data=None, headers=None): return self.request('DELETE', path, data, headers) + ### Command methods ### - ### implementation of netcli.Cli ### - - def run_commands(self, commands): + def run_commands(self, commands, **kwargs): responses = list() commands = [str(c) for c in commands] for cmd in commands: @@ -190,13 +130,12 @@ class Restconf(object): responses.append(self.execute(str(cmd))) return responses - def execute(self, command): + def execute(self, command, **kwargs): data = dict(show=command) response = self.put('global/cli', data=data) return response['results'] - - ### implementation of netcfg.Config ### + ### Config methods ### def configure(self, commands): config = list() @@ -216,3 +155,55 @@ class Restconf(object): self.put('/api/v1/global/save-config') Restconf = register_transport('restconf')(Restconf) + + +class Cli(CliBase): + + CLI_PROMPTS_RE = [ + re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), + re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$") + ] + + CLI_ERRORS_RE = [ + re.compile(r"% ?Error"), + re.compile(r"% ?Bad secret"), + re.compile(r"invalid input", re.I), + re.compile(r"(?:incomplete|ambiguous) command", re.I), + re.compile(r"connection timed out", re.I), + re.compile(r"[^\r\n]+ not found", re.I), + re.compile(r"'[^']' +returned error code: ?\d+"), + ] + + NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) + + def connect(self, params, **kwargs): + super(Cli, self).connect(params, kickstart=False, **kwargs) + self.shell.send('terminal length 0') + + def authorize(self, params, **kwargs): + passwd = params['auth_pass'] + self.execute(Command('enable', prompt=self.NET_PASSWD_RE, response=passwd)) + + ### Config methods ### + + def configure(self, commands, **kwargs): + cmds = ['configure terminal'] + cmds.extend(to_list(commands)) + if cmds[-1] != 'end': + cmds.append('end') + responses = self.execute(cmds) + return responses[1:] + + def get_config(self, include_defaults=False, **kwargs): + cmd = 'show running-config' + if include_defaults: + cmd += ' all' + return self.execute([cmd])[0] + + def load_config(self, commands, **kwargs): + return self.configure(commands) + + def save_config(self): + self.execute(['copy running-config startup-config']) + +Cli = register_transport('cli', default=True)(Cli) diff --git a/lib/ansible/module_utils/iosxr.py b/lib/ansible/module_utils/iosxr.py index b4910208f22..076c9233652 100644 --- a/lib/ansible/module_utils/iosxr.py +++ b/lib/ansible/module_utils/iosxr.py @@ -28,10 +28,10 @@ import re -from ansible.module_utils.network import NetworkModule, NetworkError -from ansible.module_utils.network import Command -from ansible.module_utils.shell import CliBase +from ansible.module_utils.netcli import Command +from ansible.module_utils.network import NetworkError, NetworkModule from ansible.module_utils.network import register_transport, to_list +from ansible.module_utils.shell import CliBase class Cli(CliBase): @@ -55,17 +55,9 @@ class Cli(CliBase): def connect(self, params, **kwargs): super(Cli, self).connect(params, kickstart=False, **kwargs) - self.shell.send(['terminal length 0', - 'terminal exec prompt no-timestamp']) - - ### implementation of netcli.Cli ### - - def run_commands(self, commands): - cmds = to_list(commands) - responses = self.execute(cmds) - return responses + self.shell.send(['terminal length 0', 'terminal exec prompt no-timestamp']) - ### implementation of netcfg.Config ### + ### Config methods ### def configure(self, commands, **kwargs): cmds = ['configure terminal'] diff --git a/lib/ansible/module_utils/junos.py b/lib/ansible/module_utils/junos.py index 75829e4e615..786e415c48f 100644 --- a/lib/ansible/module_utils/junos.py +++ b/lib/ansible/module_utils/junos.py @@ -19,10 +19,11 @@ import re import shlex +import re from distutils.version import LooseVersion from ansible.module_utils.pycompat24 import get_exception -from ansible.module_utils.network import NetworkModule, NetworkError +from ansible.module_utils.network import NetworkError, NetworkModule from ansible.module_utils.network import register_transport, to_list from ansible.module_utils.shell import CliBase from ansible.module_utils.six import string_types @@ -307,4 +308,3 @@ def rpc_args(args): else: kwargs[key] = str(value) return (name, kwargs) - diff --git a/lib/ansible/module_utils/netcfg.py b/lib/ansible/module_utils/netcfg.py index 22c5f74e5c1..e10a4a02c45 100644 --- a/lib/ansible/module_utils/netcfg.py +++ b/lib/ansible/module_utils/netcfg.py @@ -28,12 +28,9 @@ import itertools import re -import shlex -import time -from ansible.module_utils.basic import BOOLEANS_TRUE, BOOLEANS_FALSE from ansible.module_utils.six import string_types -from ansible.module_utils.six.moves import zip_longest +from ansible.module_utils.six.moves import zip, zip_longest DEFAULT_COMMENT_TOKENS = ['#', '!', '/*', '*/'] @@ -46,6 +43,7 @@ def to_list(val): else: return list() + class Config(object): def __init__(self, connection): @@ -299,7 +297,7 @@ class NetworkConfig(object): if len(other.items) != len(self.items): diff.extend(self.items) else: - for ours, theirs in itertools.izip(self.items, other.items): + for ours, theirs in zip(self.items, other.items): if ours != theirs: diff.extend(self.items) break @@ -406,5 +404,3 @@ class NetworkConfig(object): item.parents = ancestors ancestors[-1].children.append(item) self.items.append(item) - - diff --git a/lib/ansible/module_utils/netcli.py b/lib/ansible/module_utils/netcli.py index fb8c9eb150f..fe321263093 100644 --- a/lib/ansible/module_utils/netcli.py +++ b/lib/ansible/module_utils/netcli.py @@ -298,4 +298,3 @@ class Conditional(object): def matches(self, value): match = re.search(value, self.value, re.M) return match is not None - diff --git a/lib/ansible/module_utils/nxos.py b/lib/ansible/module_utils/nxos.py index dde97cad3a3..31fc098c367 100644 --- a/lib/ansible/module_utils/nxos.py +++ b/lib/ansible/module_utils/nxos.py @@ -20,12 +20,10 @@ import re import collections -from ansible.module_utils.basic import json, get_exception -from ansible.module_utils.network import NetworkModule, NetworkError, ModuleStub +from ansible.module_utils.basic import json +from ansible.module_utils.network import ModuleStub, NetworkError, NetworkModule from ansible.module_utils.network import add_argument, register_transport, to_list from ansible.module_utils.shell import CliBase -from ansible.module_utils.netcfg import NetworkConfig -from ansible.module_utils.netcli import Command from ansible.module_utils.urls import fetch_url, url_argument_spec add_argument('use_ssl', dict(default=False, type='bool')) @@ -102,6 +100,8 @@ class Nxapi(object): self._nxapi_auth = None self._connected = False + ### Command methods ### + def execute(self, commands, output=None, **kwargs): commands = collections.deque(commands) output = output or self.default_output @@ -153,9 +153,7 @@ class Nxapi(object): return result - ### implemention of netcli.Cli ### - - def run_commands(self, commands): + def run_commands(self, commands, **kwargs): output = None cmds = list() responses = list() @@ -164,6 +162,7 @@ class Nxapi(object): if output and output != cmd.output: responses.extend(self.execute(cmds, output=output)) cmds = list() + output = cmd.output cmds.append(str(cmd)) @@ -173,13 +172,13 @@ class Nxapi(object): return responses - ### implemention of netcfg.Config ### + ### Config methods ### def configure(self, commands): commands = to_list(commands) return self.execute(commands, output='config') - def get_config(self, include_defaults=False): + def get_config(self, include_defaults=False, **kwargs): cmd = 'show running-config' if include_defaults: cmd += ' all' @@ -191,8 +190,6 @@ class Nxapi(object): def save_config(self, **kwargs): self.execute(['copy running-config startup-config']) - ### end netcfg.Config ### - def _jsonify(self, data): for encoding in ("utf-8", "latin-1"): try: @@ -213,8 +210,6 @@ Nxapi = register_transport('nxapi')(Nxapi) class Cli(CliBase): - NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) - CLI_PROMPTS_RE = [ re.compile(r'[\r\n]?[a-zA-Z]{1}[a-zA-Z0-9-]*[>|#|%](?:\s*)$'), re.compile(r'[\r\n]?[a-zA-Z]{1}[a-zA-Z0-9-]*\(.+\)#(?:\s*)$') @@ -233,11 +228,13 @@ class Cli(CliBase): re.compile(r"unknown command") ] + NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) + def connect(self, params, **kwargs): super(Cli, self).connect(params, kickstart=False, **kwargs) self.shell.send('terminal length 0') - ### implementation of netcli.Cli ### + ### Command methods ### def run_commands(self, commands): cmds = list(prepare_commands(commands)) @@ -253,7 +250,7 @@ class Cli(CliBase): ) return responses - ### implemented by netcfg.Config ### + ### Config methods ### def configure(self, commands, **kwargs): commands = prepare_config(commands) diff --git a/lib/ansible/module_utils/openswitch.py b/lib/ansible/module_utils/openswitch.py index 25209f42920..e156daa5e7d 100644 --- a/lib/ansible/module_utils/openswitch.py +++ b/lib/ansible/module_utils/openswitch.py @@ -29,7 +29,7 @@ except ImportError: HAS_OPS = False from ansible.module_utils.basic import json, json_dict_bytes_to_unicode -from ansible.module_utils.network import NetworkModule, ModuleStub, NetworkError +from ansible.module_utils.network import ModuleStub, NetworkError, NetworkModule from ansible.module_utils.network import add_argument, register_transport, to_list from ansible.module_utils.shell import CliBase from ansible.module_utils.urls import fetch_url, url_argument_spec @@ -78,12 +78,19 @@ class Response(object): class Rest(object): + DEFAULT_HEADERS = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + def __init__(self): self.url = None self.url_args = ModuleStub(url_argument_spec(), self._error) - self.headers = dict({'Content-Type': 'application/json', 'Accept': 'application/json'}) - self.default_output = 'json' + + self.headers = self.DEFAULT_HEADERS + self._connected = False + self.default_output = 'json' def _error(self, msg): raise NetworkError(msg, url=self.url) @@ -107,7 +114,7 @@ class Rest(object): port = 80 baseurl = '%s://%s:%s' % (proto, host, port) - headers = dict({'Content-Type': 'application/x-www-form-urlencoded'}) + headers = {'Content-Type': 'application/x-www-form-urlencoded'} # Get a cookie and save it the rest of the operations. url = '%s/%s' % (baseurl, 'login') data = 'username=%s&password=%s' % (params['username'], params['password']) @@ -124,6 +131,8 @@ class Rest(object): def authorize(self, params, **kwargs): raise NotImplementedError + ### REST methods ### + def _url_builder(self, path): if path[0] == '/': path = path[1:] @@ -153,9 +162,13 @@ class Rest(object): def delete(self, path, data=None, headers=None): return self.request('DELETE', path, data, headers) + ### Command methods ### + def run_commands(self, commands): raise NotImplementedError + ### Config methods ### + def configure(self, commands): path = '/system/full-configuration' return self.put(path, data=commands) @@ -190,8 +203,6 @@ Rest = register_transport('rest')(Rest) class Cli(CliBase): - NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) - CLI_PROMPTS_RE = [ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$") @@ -207,12 +218,9 @@ class Cli(CliBase): re.compile(r"'[^']' +returned error code: ?\d+"), ] - ### implementation of netcli.Cli - - def run_commands(self, commands): - return self.execute(to_list(commands)) + NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) - ### implementation of netcfg.Config + ### Config methods ### def configure(self, commands, **kwargs): cmds = ['configure terminal'] @@ -225,7 +233,7 @@ class Cli(CliBase): def get_config(self): return self.execute('show running-config')[0] - def load_config(self, commands): + def load_config(self, commands, **kwargs): return self.configure(commands) def save_config(self):