summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Sprygada <privateip@users.noreply.github.com>2017-07-17 21:23:38 -0400
committerGitHub <noreply@github.com>2017-07-17 21:23:38 -0400
commit8e2dcaf9f64abfda3077a2cf5ead590f5f6dc91c (patch)
tree6896d8f97921eee7d799b84ff49d16e3cacf4d21
parente976f299f81381997c0c1b89f0bafb8e30f0fefc (diff)
downloadansible-8e2dcaf9f64abfda3077a2cf5ead590f5f6dc91c.tar.gz
update asa to use network_cli connection plugin (#26899)
* WIP update asa to use network_cli connection plugin * add asa.py to cliconf plugins * update asa.py terminal plugin to support regexp and events * update constants to map asa modules to asa action handler * update asa action handler to implement persistent connections * update asa shared module to use persistent connections * update asa_command module to use new connection * fixed pep8 issues
-rw-r--r--lib/ansible/config/data/config.yml30
-rw-r--r--lib/ansible/module_utils/asa.py171
-rw-r--r--lib/ansible/modules/network/asa/asa_command.py115
-rw-r--r--lib/ansible/plugins/action/asa.py111
-rw-r--r--lib/ansible/plugins/cliconf/asa.py78
-rw-r--r--lib/ansible/plugins/terminal/asa.py42
6 files changed, 367 insertions, 180 deletions
diff --git a/lib/ansible/config/data/config.yml b/lib/ansible/config/data/config.yml
index af5f3cde25..197edc3231 100644
--- a/lib/ansible/config/data/config.yml
+++ b/lib/ansible/config/data/config.yml
@@ -448,7 +448,7 @@ DEFAULT_BECOME_ASK_PASS:
vars: []
yaml: {key: privilege_escalation.become_ask_pass}
DEFAULT_BECOME_EXE:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_BECOME_EXE}]
ini:
@@ -456,7 +456,7 @@ DEFAULT_BECOME_EXE:
vars: []
yaml: {key: privilege_escalation.become_exe}
DEFAULT_BECOME_FLAGS:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_BECOME_FLAGS}]
ini:
@@ -542,7 +542,7 @@ DEFAULT_EXECUTABLE:
vars: []
yaml: {key: defaults.executable}
DEFAULT_FACT_PATH:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_FACT_PATH}]
ini:
@@ -650,7 +650,7 @@ DEFAULT_INVENTORY_PLUGIN_PATH:
vars: []
yaml: {key: defaults.inventory_plugins}
DEFAULT_JINJA2_EXTENSIONS:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_JINJA2_EXTENSIONS}]
ini:
@@ -797,7 +797,7 @@ DEFAULT_NO_TARGET_SYSLOG:
vars: []
yaml: {key: defaults.no_target_syslog}
DEFAULT_NULL_REPRESENTATION:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_NULL_REPRESENTATION}]
ini:
@@ -815,7 +815,7 @@ DEFAULT_POLL_INTERVAL:
vars: []
yaml: {key: defaults.poll_interval}
DEFAULT_PRIVATE_KEY_FILE:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_PRIVATE_KEY_FILE}]
ini:
@@ -833,7 +833,7 @@ DEFAULT_PRIVATE_ROLE_VARS:
vars: []
yaml: {key: defaults.private_role_vars}
DEFAULT_REMOTE_PORT:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_REMOTE_PORT}]
ini:
@@ -851,7 +851,7 @@ DEFAULT_REMOTE_TMP:
- name: ansible_remote_tmp
yaml: {key: defaults.remote_tmp}
DEFAULT_REMOTE_USER:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_REMOTE_USER}]
ini:
@@ -904,7 +904,7 @@ DEFAULT_SQUASH_ACTIONS:
vars: []
yaml: {key: defaults.squash_actions}
DEFAULT_SSH_TRANSFER_METHOD:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_SSH_TRANSFER_METHOD}]
ini:
@@ -955,7 +955,7 @@ DEFAULT_SUDO:
vars: []
yaml: {key: defaults.sudo}
DEFAULT_SUDO_EXE:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_SUDO_EXE}]
ini:
@@ -979,7 +979,7 @@ DEFAULT_SUDO_USER:
vars: []
yaml: {key: defaults.sudo_user}
DEFAULT_SU_EXE:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_SU_EXE}]
ini:
@@ -987,7 +987,7 @@ DEFAULT_SU_EXE:
vars: []
yaml: {key: defaults.su_exe}
DEFAULT_SU_FLAGS:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_SU_FLAGS}]
ini:
@@ -1154,7 +1154,7 @@ GALAXY_IGNORE_CERTS:
vars: []
yaml: {key: galaxy.ignore_certs}
GALAXY_ROLE_SKELETON:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_GALAXY_ROLE_SKELETON}]
ini:
@@ -1243,7 +1243,7 @@ MERGE_MULTIPLE_CLI_TAGS:
vars: []
yaml: {key: defaults.merge_multiple_cli_tags}
NETWORK_GROUP_MODULES:
- default: [eos, nxos, ios, iosxr, junos, ce, vyos, sros, dellos9, dellos10, dellos6]
+ default: [eos, nxos, ios, iosxr, junos, ce, vyos, sros, dellos9, dellos10, dellos6, asa]
desc: 'TODO: write it'
env: [{name: NETWORK_GROUP_MODULES}]
ini:
@@ -1282,7 +1282,7 @@ PARAMIKO_LOOK_FOR_KEYS:
vars: []
yaml: {key: paramiko_connection.look_for_keys}
PARAMIKO_PROXY_COMMAND:
- default:
+ default:
desc: 'TODO: write it'
env: [{name: ANSIBLE_PARAMIKO_PROXY_COMMAND}]
ini:
diff --git a/lib/ansible/module_utils/asa.py b/lib/ansible/module_utils/asa.py
index 97d60815fd..d4b3880483 100644
--- a/lib/ansible/module_utils/asa.py
+++ b/lib/ansible/module_utils/asa.py
@@ -4,8 +4,7 @@
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
-# Copyright (c) 2016 Peter Sprygada, <psprygada@ansible.com>
-# Copyright (c) 2016 Patrick Ogenstad, <@ogenstad>
+# (c) 2016 Red Hat Inc.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
@@ -26,90 +25,126 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import env_fallback, return_values
+from ansible.module_utils.network_common import to_list, EntityCollection
+from ansible.module_utils.connection import Connection
-import re
+_DEVICE_CONFIGS = {}
+_CONNECTION = None
-from ansible.module_utils.network import NetworkError, NetworkModule
-from ansible.module_utils.network import add_argument, register_transport
-from ansible.module_utils.network import to_list
-from ansible.module_utils.shell import CliBase
-from ansible.module_utils.netcli import Command
+asa_argument_spec = {
+ 'host': dict(),
+ 'port': dict(type='int'),
+ 'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
+ 'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
+ 'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
+ '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'),
+ 'provider': dict(type='dict'),
+ 'context': dict()
+}
-add_argument('context', dict(required=False))
+command_spec = {
+ 'command': dict(key=True),
+ 'prompt': dict(),
+ 'answer': dict()
+}
-class Cli(CliBase):
+def get_argspec():
+ return asa_argument_spec
- CLI_PROMPTS_RE = [
- re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
- re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
- ]
- CLI_ERRORS_RE = [
- re.compile(r"error:", re.I),
- re.compile(r"^Removing.* not allowed")
- ]
+def check_args(module):
+ provider = module.params['provider'] or {}
- NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
+ for key in asa_argument_spec:
+ if key not in ['provider', 'authorize'] and module.params[key]:
+ module.warn('argument %s has been deprecated and will be removed in a future version' % key)
- def __init__(self, *args, **kwargs):
+ if provider:
+ for param in ('auth_pass', 'password'):
+ if provider.get(param):
+ module.no_log_values.update(return_values(provider[param]))
- super(Cli, self).__init__(*args, **kwargs)
- self.default_output = 'text'
- def connect(self, params, **kwargs):
- super(Cli, self).connect(params, kickstart=False, **kwargs)
+def get_connection(module):
+ global _CONNECTION
+ if _CONNECTION:
+ return _CONNECTION
+ _CONNECTION = Connection(module)
- if params['context']:
- self.change_context(params, **kwargs)
+ context = module.params['context']
- def authorize(self, params, **kwargs):
- passwd = params['auth_pass']
- errors = self.shell.errors
- # Disable errors (if already in enable mode)
- self.shell.errors = []
- cmd = Command('enable', prompt=self.NET_PASSWD_RE, response=passwd)
- self.execute([cmd, 'no terminal pager'])
- # Reapply error handling
- self.shell.errors = errors
-
- def change_context(self, params):
- context = params['context']
+ if context:
if context == 'system':
command = 'changeto system'
else:
command = 'changeto context %s' % context
+ _CONNECTION.get(command)
+
+ return _CONNECTION
+
+
+def to_commands(module, commands):
+ assert isinstance(commands, list), 'argument must be of type <list>'
+
+ transform = EntityCollection(module, command_spec)
+ commands = transform(commands)
+
+ for index, item in enumerate(commands):
+ if module.check_mode and not item['command'].startswith('show'):
+ module.warn('only show commands are supported when using check '
+ 'mode, not executing `%s`' % item['command'])
+
+ return commands
+
+
+def run_commands(module, commands, check_rc=True):
+ commands = to_commands(module, to_list(commands))
+ connection = get_connection(module)
+
+ responses = list()
+
+ for cmd in commands:
+ out = connection.get(**cmd)
+ responses.append(to_text(out, errors='surrogate_then_replace'))
+
+ return responses
+
+
+def get_config(module, flags=[]):
+ cmd = 'show running-config '
+ cmd += ' '.join(flags)
+ cmd = cmd.strip()
+
+ try:
+ return _DEVICE_CONFIGS[cmd]
+ except KeyError:
+ conn = get_connection(module)
+ out = conn.get(cmd)
+ cfg = to_text(out, errors='surrogate_then_replace').strip()
+ _DEVICE_CONFIGS[cmd] = cfg
+ return cfg
+
+
+def load_config(module, config):
+ conn = get_connection(module)
+ conn.edit_config(config)
- self.execute(command)
-
- # Config methods
-
- def configure(self, commands):
- cmds = ['configure terminal']
- cmds.extend(to_list(commands))
- if cmds[-1] == 'exit':
- cmds[-1] = 'end'
- elif cmds[-1] != 'end':
- cmds.append('end')
- responses = self.execute(cmds)
- return responses[1:]
-
- def get_config(self, include=None):
- if include not in [None, 'defaults', 'passwords']:
- raise ValueError('include must be one of None, defaults, passwords')
- cmd = 'show running-config'
- if include == 'passwords':
- cmd = 'more system:running-config'
- elif include == 'defaults':
- cmd = 'show running-config all'
- else:
- cmd = 'show running-config'
- return self.run_commands(cmd)[0]
- def load_config(self, commands):
- return self.configure(commands)
+def get_defaults_flag(module):
+ rc, out, err = exec_command(module, 'show running-config ?')
+ out = to_text(out, errors='surrogate_then_replace')
- def save_config(self):
- self.execute(['write memory'])
+ commands = set()
+ for line in out.splitlines():
+ if line:
+ commands.add(line.strip().split()[0])
-Cli = register_transport('cli', default=True)(Cli)
+ if 'all' in commands:
+ return 'all'
+ else:
+ return 'full'
diff --git a/lib/ansible/modules/network/asa/asa_command.py b/lib/ansible/modules/network/asa/asa_command.py
index c28df85d28..5e927cf2c6 100644
--- a/lib/ansible/modules/network/asa/asa_command.py
+++ b/lib/ansible/modules/network/asa/asa_command.py
@@ -133,28 +133,18 @@ failed_conditions:
type: list
sample: ['...', '...']
"""
-from ansible.module_utils.basic import get_exception
-from ansible.module_utils.netcli import CommandRunner
-from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
-from ansible.module_utils.asa import NetworkModule, NetworkError
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.asa import asa_argument_spec, check_args
+from ansible.module_utils.asa import run_commands
+from ansible.module_utils.six import string_types
-VALID_KEYS = ['command', 'prompt', 'response']
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
-def parse_commands(module):
- for cmd in module.params['commands']:
- if isinstance(cmd, basestring):
- cmd = dict(command=cmd, output=None)
- elif 'command' not in cmd:
- module.fail_json(msg='command keyword argument is required')
- elif not set(cmd.keys()).issubset(VALID_KEYS):
- module.fail_json(msg='unknown keyword specified')
- yield cmd
def main():
spec = dict(
@@ -168,59 +158,48 @@ def main():
interval=dict(default=1, type='int')
)
- module = NetworkModule(argument_spec=spec,
- connect_on_load=False,
- supports_check_mode=True)
-
- commands = list(parse_commands(module))
- conditionals = module.params['wait_for'] or list()
-
- warnings = list()
-
- runner = CommandRunner(module)
-
- for cmd in commands:
- if module.check_mode and not cmd['command'].startswith('show'):
- warnings.append('only show commands are supported when using '
- 'check mode, not executing `%s`' % cmd['command'])
- else:
- if cmd['command'].startswith('conf'):
- module.fail_json(msg='asa_command does not support running '
- 'config mode commands. Please use '
- 'asa_config instead')
- try:
- runner.add_command(**cmd)
- except AddCommandError:
- exc = get_exception()
- warnings.append('duplicate command detected: %s' % cmd)
-
- for item in conditionals:
- runner.add_conditional(item)
-
- runner.retries = module.params['retries']
- runner.interval = module.params['interval']
- runner.match = module.params['match']
-
- try:
- runner.run()
- except FailedConditionsError:
- exc = get_exception()
- module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)
- except NetworkError:
- exc = get_exception()
- module.fail_json(msg=str(exc))
-
- result = dict(changed=False, stdout=list())
-
- for cmd in commands:
- try:
- output = runner.get_command(cmd['command'])
- except ValueError:
- output = 'command not executed due to check_mode, see warnings'
- result['stdout'].append(output)
-
- result['warnings'] = warnings
- result['stdout_lines'] = list(to_lines(result['stdout']))
+ spec.update(asa_argument_spec)
+
+ module = AnsibleModule(argument_spec=spec, supports_check_mode=True)
+ check_args(module)
+
+ result = {'changed': False}
+
+ wait_for = module.params['wait_for'] or list()
+ conditionals = [Conditional(c) for c in wait_for]
+
+ commands = module.params['commands']
+ retries = module.params['retries']
+ interval = module.params['interval']
+ match = module.params['match']
+
+ while retries > 0:
+ responses = run_commands(module, commands)
+
+ for item in list(conditionals):
+ if item(responses):
+ if match == 'any':
+ conditionals = list()
+ break
+ conditionals.remove(item)
+
+ if not conditionals:
+ break
+
+ time.sleep(interval)
+ retries -= 1
+
+ if conditionals:
+ failed_conditions = [item.raw for item in conditionals]
+ msg = 'One or more conditional statements have not be satisfied'
+ module.fail_json(msg=msg, failed_conditions=failed_conditions)
+
+
+ result.update({
+ 'changed': False,
+ 'stdout': responses,
+ 'stdout_lines': list(to_lines(responses))
+ })
module.exit_json(**result)
diff --git a/lib/ansible/plugins/action/asa.py b/lib/ansible/plugins/action/asa.py
new file mode 100644
index 0000000000..3a9eff5579
--- /dev/null
+++ b/lib/ansible/plugins/action/asa.py
@@ -0,0 +1,111 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+import copy
+import json
+
+from ansible.plugins.action.normal import ActionModule as _ActionModule
+from ansible.module_utils.basic import AnsibleFallbackNotFound
+from ansible.module_utils.asa import asa_argument_spec
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.connection import request_builder
+
+try:
+ from __main__ import display
+except ImportError:
+ from ansible.utils.display import Display
+ display = Display()
+
+
+class ActionModule(_ActionModule):
+
+ def run(self, tmp=None, task_vars=None):
+
+ if self._play_context.connection != 'local':
+ return dict(
+ failed=True,
+ msg='invalid connection specified, expected connection=local, '
+ 'got %s' % self._play_context.connection
+ )
+
+ provider = self.load_provider()
+
+ pc = copy.deepcopy(self._play_context)
+ pc.connection = 'network_cli'
+ pc.network_os = 'asa'
+ pc.remote_addr = provider['host'] or self._play_context.remote_addr
+ pc.port = provider['port'] or self._play_context.port or 22
+ pc.remote_user = provider['username'] or self._play_context.connection_user
+ pc.password = provider['password'] or self._play_context.password
+ pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file
+ pc.timeout = provider['timeout'] or self._play_context.timeout
+ pc.become = provider['authorize'] or False
+ pc.become_pass = provider['auth_pass']
+
+ display.vvv('using connection plugin %s' % pc.connection, pc.remote_addr)
+ connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
+
+ socket_path = connection.run()
+
+ display.vvvv('socket_path: %s' % socket_path, pc.remote_addr)
+ if not socket_path:
+ return {'failed': True,
+ 'msg': 'unable to open shell. Please see: ' +
+ 'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'}
+
+ task_vars['ansible_socket'] = socket_path
+
+ result = super(ActionModule, self).run(tmp, task_vars)
+
+ # take the shell out of enable mode
+ if pc.become:
+ req = json.dumps(request_builder('get', 'disable'))
+ out = connection.exec_command(req)
+
+ return result
+
+ def load_provider(self):
+ provider = self._task.args.get('provider', {})
+ for key, value in iteritems(asa_argument_spec):
+ if key != 'provider' and key not in provider:
+ if key in self._task.args:
+ provider[key] = self._task.args[key]
+ elif 'fallback' in value:
+ provider[key] = self._fallback(value['fallback'])
+ elif key not in provider:
+ provider[key] = None
+ return provider
+
+ def _fallback(self, fallback):
+ strategy = fallback[0]
+ args = []
+ kwargs = {}
+
+ for item in fallback[1:]:
+ if isinstance(item, dict):
+ kwargs = item
+ else:
+ args = item
+ try:
+ return strategy(*args, **kwargs)
+ except AnsibleFallbackNotFound:
+ pass
diff --git a/lib/ansible/plugins/cliconf/asa.py b/lib/ansible/plugins/cliconf/asa.py
new file mode 100644
index 0000000000..fde32c69a2
--- /dev/null
+++ b/lib/ansible/plugins/cliconf/asa.py
@@ -0,0 +1,78 @@
+#
+# (c) 2017 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import json
+
+from itertools import chain
+
+from ansible.module_utils._text import to_bytes, to_text
+from ansible.module_utils.network_common import to_list
+from ansible.plugins.cliconf import CliconfBase, enable_mode
+
+
+class Cliconf(CliconfBase):
+
+ def get_device_info(self):
+ device_info = {}
+
+ device_info['network_os'] = 'asa'
+ reply = self.get(b'show version')
+ data = to_text(reply, errors='surrogate_or_strict').strip()
+
+ match = re.search(r'Version (\S+),', data)
+ if match:
+ device_info['network_os_version'] = match.group(1)
+
+ match = re.search(r'^Model Id:\s+(.+) \(revision', data, re.M)
+ if match:
+ device_info['network_os_model'] = match.group(1)
+
+ match = re.search(r'^(.+) up', data, re.M)
+ if match:
+ device_info['network_os_hostname'] = match.group(1)
+
+ return device_info
+
+ @enable_mode
+ def get_config(self, source='running'):
+ if source not in ('running', 'startup'):
+ return self.invalid_params("fetching configuration from %s is not supported" % source)
+ if source == 'running':
+ cmd = b'show running-config all'
+ else:
+ cmd = b'show startup-config'
+ return self.send_command(cmd)
+
+ @enable_mode
+ def edit_config(self, command):
+ for cmd in chain([b'configure terminal'], to_list(command), [b'end']):
+ self.send_command(cmd)
+
+ def get(self, *args, **kwargs):
+ return self.send_command(*args, **kwargs)
+
+ def get_capabilities(self):
+ result = {}
+ result['rpc'] = self.get_base_rpc()
+ result['network_api'] = 'cliconf'
+ result['device_info'] = self.get_device_info()
+ return json.dumps(result)
diff --git a/lib/ansible/plugins/terminal/asa.py b/lib/ansible/plugins/terminal/asa.py
index 9fc117804b..2186900f15 100644
--- a/lib/ansible/plugins/terminal/asa.py
+++ b/lib/ansible/plugins/terminal/asa.py
@@ -22,8 +22,9 @@ __metaclass__ = type
import re
import json
-from ansible.plugins.terminal import TerminalBase
from ansible.errors import AnsibleConnectionFailure
+from ansible.module_utils._text import to_text, to_bytes
+from ansible.plugins.terminal import TerminalBase
class TerminalModule(TerminalBase):
@@ -34,40 +35,23 @@ class TerminalModule(TerminalBase):
]
terminal_stderr_re = [
- re.compile(r"% ?Error"),
- re.compile(r"^% \w+", re.M),
- 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+"),
+ re.compile(r"error:", re.I),
+ re.compile(r"^Removing.* not allowed")
]
- def authorize(self, passwd=None):
- if self._get_prompt().endswith('#'):
+ def on_authorize(self, passwd=None):
+ if self._get_prompt().endswith(b'#'):
return
- cmd = {'command': 'enable'}
+ cmd = {u'command': u'enable'}
if passwd:
- cmd['prompt'] = r"[\r\n]?password: $"
- cmd['answer'] = passwd
+ # Note: python-3.5 cannot combine u"" and r"" together. Thus make
+ # an r string and use to_text to ensure it's text on both py2 and py3.
+ cmd[u'prompt'] = to_text(r"[\r\n]?password: $", errors='surrogate_or_strict')
+ cmd[u'answer'] = passwd
try:
- self._exec_cli_command(json.dumps(cmd))
- self._exec_cli_command('terminal pager 0')
+ self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict'))
+ self._exec_cli_command(u'no terminal pager')
except AnsibleConnectionFailure:
raise AnsibleConnectionFailure('unable to elevate privilege to enable mode')
-
- def on_deauthorize(self):
- prompt = self._get_prompt()
- if prompt is None:
- # if prompt is None most likely the terminal is hung up at a prompt
- return
-
- if '(config' in prompt:
- self._exec_cli_command('end')
- self._exec_cli_command('disable')
-
- elif prompt.endswith('#'):
- self._exec_cli_command('disable')