mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-24 11:21:25 -07:00
adds two new plugins that use ansible-connection for persistence (#18572)
* adds new connection plugin `network_cli` which builds on paramiko * adds new plugin `terminal` used for manipulating network_cli terminals * adds new field to play_context `network_os` settable as ansible_network_os This commit adds the plugins necesary to establish a persistent cli connection to network devices of ssh. It builds on the paramiko connection plugin to create a shell environment that will persistent through ansible-connection. The `newtork_cli` plugin then uses the network_os in the instance of PlayContext to load the appropriate network OS environment plugin for handling opening and closing of shells as well as privilege escalation.
This commit is contained in:
parent
54c5ea29bb
commit
9aa8547016
13 changed files with 764 additions and 2 deletions
|
@ -60,6 +60,7 @@ MAGIC_VARIABLE_MAPPING = dict(
|
|||
private_key_file = ('ansible_ssh_private_key_file', 'ansible_private_key_file'),
|
||||
pipelining = ('ansible_ssh_pipelining', 'ansible_pipelining'),
|
||||
shell = ('ansible_shell_type',),
|
||||
network_os = ('ansible_network_os',),
|
||||
become = ('ansible_become',),
|
||||
become_method = ('ansible_become_method',),
|
||||
become_user = ('ansible_become_user',),
|
||||
|
@ -164,6 +165,7 @@ class PlayContext(Base):
|
|||
_private_key_file = FieldAttribute(isa='string', default=C.DEFAULT_PRIVATE_KEY_FILE)
|
||||
_timeout = FieldAttribute(isa='int', default=C.DEFAULT_TIMEOUT)
|
||||
_shell = FieldAttribute(isa='string')
|
||||
_network_os = FieldAttribute(isa='string')
|
||||
_ssh_args = FieldAttribute(isa='string', default=C.ANSIBLE_SSH_ARGS)
|
||||
_ssh_common_args = FieldAttribute(isa='string')
|
||||
_sftp_extra_args = FieldAttribute(isa='string')
|
||||
|
|
|
@ -509,3 +509,11 @@ strategy_loader = PluginLoader(
|
|||
'strategy_plugins',
|
||||
required_base_class='StrategyBase',
|
||||
)
|
||||
|
||||
terminal_loader = PluginLoader(
|
||||
'TerminalModule',
|
||||
'ansible.plugins.terminal',
|
||||
'terminal_plugins',
|
||||
'terminal_plugins'
|
||||
)
|
||||
|
||||
|
|
190
lib/ansible/plugins/connection/network_cli.py
Normal file
190
lib/ansible/plugins/connection/network_cli.py
Normal file
|
@ -0,0 +1,190 @@
|
|||
#
|
||||
# (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 re
|
||||
import socket
|
||||
import json
|
||||
import signal
|
||||
import datetime
|
||||
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils.six.moves import StringIO
|
||||
from ansible.plugins import terminal_loader
|
||||
from ansible.plugins.connection.paramiko_ssh import Connection as _Connection
|
||||
|
||||
|
||||
class Connection(_Connection):
|
||||
''' CLI SSH based connections on Paramiko '''
|
||||
|
||||
transport = 'network_cli'
|
||||
has_pipelining = False
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
||||
assert self._play_context.network_os, 'ansible_network_os must be set'
|
||||
|
||||
self._terminal = terminal_loader.get(self._play_context.network_os, self)
|
||||
if not self._terminal:
|
||||
raise AnsibleConnectionFailure('network os %s is not supported' % self._play_context.network_os)
|
||||
|
||||
self._shell = None
|
||||
|
||||
self._matched_prompt = None
|
||||
self._matched_pattern = None
|
||||
self._last_response = None
|
||||
self._history = list()
|
||||
|
||||
def update_play_context(self, play_context):
|
||||
if self._play_context.become is False and play_context.become is True:
|
||||
auth_pass = play_context.become_pass
|
||||
self._terminal.on_authorize(passwd=auth_pass)
|
||||
|
||||
elif self._play_context.become is True and not play_context.become:
|
||||
self._terminal.on_deauthorize()
|
||||
|
||||
self._play_context = play_context
|
||||
|
||||
def _connect(self):
|
||||
super(Connection, self)._connect()
|
||||
return (0, 'connected', '')
|
||||
|
||||
def open_shell(self, timeout=10):
|
||||
self._shell = self.ssh.invoke_shell()
|
||||
self._shell.settimeout(self._play_context.timeout)
|
||||
|
||||
self.receive()
|
||||
|
||||
if self._shell:
|
||||
self._terminal.on_open_shell()
|
||||
|
||||
if hasattr(self._play_context, 'become'):
|
||||
if self._play_context.become:
|
||||
auth_pass = self._play_context.become_pass
|
||||
self._terminal.on_authorize(passwd=auth_pass)
|
||||
|
||||
def close(self):
|
||||
self.close_shell()
|
||||
super(Connection, self).close()
|
||||
|
||||
def close_shell(self):
|
||||
if self._shell:
|
||||
self._terminal.on_close_shell()
|
||||
|
||||
if self._terminal.supports_multiplexing and self._shell:
|
||||
self._shell.close()
|
||||
self._shell = None
|
||||
|
||||
return (0, 'shell closed', '')
|
||||
|
||||
def receive(self, obj=None):
|
||||
recv = StringIO()
|
||||
handled = False
|
||||
|
||||
self._matched_prompt = None
|
||||
|
||||
while True:
|
||||
data = self._shell.recv(256)
|
||||
|
||||
recv.write(data)
|
||||
recv.seek(recv.tell() - 256)
|
||||
|
||||
window = self._strip(recv.read())
|
||||
|
||||
if obj and (obj.get('prompt') and not handled):
|
||||
handled = self._handle_prompt(window, obj)
|
||||
|
||||
if self._find_prompt(window):
|
||||
self._last_response = recv.getvalue()
|
||||
resp = self._strip(self._last_response)
|
||||
return self._sanitize(resp, obj)
|
||||
|
||||
def send(self, obj):
|
||||
try:
|
||||
command = obj['command']
|
||||
self._history.append(command)
|
||||
self._shell.sendall('%s\r' % command)
|
||||
return self.receive(obj)
|
||||
except (socket.timeout, AttributeError):
|
||||
raise AnsibleConnectionFailure("timeout trying to send command: %s" % command.strip())
|
||||
|
||||
def _strip(self, data):
|
||||
for regex in self._terminal.ansi_re:
|
||||
data = regex.sub('', data)
|
||||
return data
|
||||
|
||||
def _handle_prompt(self, resp, obj):
|
||||
prompt = re.compile(obj['prompt'], re.I)
|
||||
answer = obj['answer']
|
||||
match = prompt.search(resp)
|
||||
if match:
|
||||
self._shell.sendall('%s\r' % answer)
|
||||
return True
|
||||
|
||||
def _sanitize(self, resp, obj=None):
|
||||
cleaned = []
|
||||
command = obj.get('command') if obj else None
|
||||
for line in resp.splitlines():
|
||||
if (command and line.startswith(command.strip())) or self._find_prompt(line):
|
||||
continue
|
||||
cleaned.append(line)
|
||||
return str("\n".join(cleaned)).strip()
|
||||
|
||||
def _find_prompt(self, response):
|
||||
for regex in self._terminal.terminal_errors_re:
|
||||
if regex.search(response):
|
||||
raise AnsibleConnectionFailure(response)
|
||||
|
||||
for regex in self._terminal.terminal_prompts_re:
|
||||
match = regex.search(response)
|
||||
if match:
|
||||
self._matched_pattern = regex.pattern
|
||||
self._matched_prompt = match.group()
|
||||
return True
|
||||
|
||||
def alarm_handler(self, signum, frame):
|
||||
self.close_shell()
|
||||
|
||||
def exec_command(self, cmd):
|
||||
''' {'command': <str>, 'prompt': <str>, 'answer': <str>} '''
|
||||
|
||||
try:
|
||||
obj = json.loads(cmd)
|
||||
except ValueError:
|
||||
obj = {'command': str(cmd).strip()}
|
||||
|
||||
if obj['command'] == 'close_shell()':
|
||||
return self.close_shell()
|
||||
elif obj['command'] == 'prompt()':
|
||||
return (0, self._matched_prompt, '')
|
||||
elif obj['command'] == 'history()':
|
||||
return (0, self._history, '')
|
||||
|
||||
try:
|
||||
if self._shell is None:
|
||||
self.open_shell()
|
||||
except AnsibleConnectionFailure as exc:
|
||||
return (1, '', str(exc))
|
||||
|
||||
try:
|
||||
out = self.send(obj)
|
||||
return (0, out, '')
|
||||
except (AnsibleConnectionFailure, ValueError) as exc:
|
||||
return (1, '', str(exc))
|
|
@ -421,8 +421,9 @@ class Connection(ConnectionBase):
|
|||
SSH_CONNECTION_CACHE.pop(cache_key, None)
|
||||
SFTP_CONNECTION_CACHE.pop(cache_key, None)
|
||||
|
||||
if self.sftp is not None:
|
||||
self.sftp.close()
|
||||
if hasattr(self, 'sftp'):
|
||||
if self.sftp is not None:
|
||||
self.sftp.close()
|
||||
|
||||
if C.HOST_KEY_CHECKING and C.PARAMIKO_RECORD_HOST_KEYS and self._any_keys_added():
|
||||
|
||||
|
|
71
lib/ansible/plugins/terminal/__init__.py
Normal file
71
lib/ansible/plugins/terminal/__init__.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
#
|
||||
# (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 re
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from ansible.compat.six import with_metaclass
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalBase(with_metaclass(ABCMeta, object)):
|
||||
'''
|
||||
A base class for implementing cli connections
|
||||
'''
|
||||
|
||||
terminalprompts_re = []
|
||||
|
||||
terminalerrors_re = []
|
||||
|
||||
ansi_re = [
|
||||
re.compile(r'(\x1b\[\?1h\x1b=)'),
|
||||
re.compile(r'\x08.')
|
||||
]
|
||||
|
||||
supports_multiplexing = True
|
||||
|
||||
def __init__(self, connection):
|
||||
self._connection = connection
|
||||
|
||||
def _exec_cli_command(self, cmd, check_rc=True):
|
||||
rc, out, err = self._connection.exec_command(cmd)
|
||||
if check_rc and rc != 0:
|
||||
raise AnsibleConnectionFailure(err)
|
||||
return rc, out, err
|
||||
|
||||
def _get_prompt(self):
|
||||
for cmd in ['\n', 'prompt()']:
|
||||
rc, out, err = self._exec_cli_command(cmd)
|
||||
return out
|
||||
|
||||
def on_open_shell(self):
|
||||
pass
|
||||
|
||||
def on_close_shell(self):
|
||||
pass
|
||||
|
||||
def on_authorize(self, passwd=None):
|
||||
pass
|
||||
|
||||
def on_deauthorize(self):
|
||||
pass
|
||||
|
75
lib/ansible/plugins/terminal/asa.py
Normal file
75
lib/ansible/plugins/terminal/asa.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
#
|
||||
# (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 re
|
||||
import json
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
|
||||
]
|
||||
|
||||
terminal_errors_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+"),
|
||||
]
|
||||
|
||||
def authorize(self, passwd=None):
|
||||
if self._get_prompt().endswith('#'):
|
||||
return
|
||||
|
||||
cmd = {'command': 'enable'}
|
||||
if passwd:
|
||||
cmd['prompt'] = r"[\r\n]?password: $"
|
||||
cmd['answer'] = passwd
|
||||
|
||||
try:
|
||||
self._exec_cli_command(json.dumps(cmd))
|
||||
self._exec_cli_command('terminal pager 0')
|
||||
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')
|
||||
|
||||
|
82
lib/ansible/plugins/terminal/eos.py
Normal file
82
lib/ansible/plugins/terminal/eos.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
#
|
||||
# (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 re
|
||||
import json
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
|
||||
]
|
||||
|
||||
terminal_errors_re = [
|
||||
re.compile(r"% ?Error"),
|
||||
re.compile(r"^% \w+", re.M),
|
||||
re.compile(r"% User not present"),
|
||||
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"[^\r\n]\/bin\/(?:ba)?sh")
|
||||
]
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
self._exec_cli_command('terminal length 0')
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||
|
||||
def on_authorize(self, passwd=None):
|
||||
if self._get_prompt().endswith('#'):
|
||||
return
|
||||
|
||||
cmd = {'command': 'enable'}
|
||||
if passwd:
|
||||
cmd['prompt'] = r"[\r\n]?password: $"
|
||||
cmd['answer'] = passwd
|
||||
|
||||
try:
|
||||
self._exec_cli_command(json.dumps(cmd))
|
||||
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')
|
||||
|
||||
|
82
lib/ansible/plugins/terminal/ios.py
Normal file
82
lib/ansible/plugins/terminal/ios.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
#
|
||||
# (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 re
|
||||
import json
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
|
||||
]
|
||||
|
||||
terminal_errors_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+"),
|
||||
]
|
||||
|
||||
supports_multiplexing = False
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
self._exec_cli_command('terminal length 0')
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||
|
||||
def on_authorize(self, passwd=None):
|
||||
if self._get_prompt().endswith('#'):
|
||||
return
|
||||
|
||||
cmd = {'command': 'enable'}
|
||||
if passwd:
|
||||
cmd['prompt'] = r"[\r\n]?password: $"
|
||||
cmd['answer'] = passwd
|
||||
|
||||
try:
|
||||
self._exec_cli_command(json.dumps(cmd))
|
||||
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')
|
||||
|
||||
|
55
lib/ansible/plugins/terminal/iosxr.py
Normal file
55
lib/ansible/plugins/terminal/iosxr.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
#
|
||||
# (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 re
|
||||
import json
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
|
||||
]
|
||||
|
||||
terminal_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+"),
|
||||
]
|
||||
|
||||
supports_multiplexing = False
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
for cmd in ['terminal length 0', 'terminal exec prompt no-timestamp']:
|
||||
self._connection.exec_command(cmd)
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||
|
||||
|
47
lib/ansible/plugins/terminal/junos.py
Normal file
47
lib/ansible/plugins/terminal/junos.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# (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 re
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
]
|
||||
|
||||
terminal_errors_re = [
|
||||
re.compile(r"unknown command"),
|
||||
re.compile(r"syntax error,")
|
||||
]
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
for c in ['set cli timestamp disable', 'set cli screen-length 0']:
|
||||
self._exec_cli_command(c)
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||
|
56
lib/ansible/plugins/terminal/nxos.py
Normal file
56
lib/ansible/plugins/terminal/nxos.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
#
|
||||
# (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 re
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_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*)$')
|
||||
]
|
||||
|
||||
terminal_errors_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"syntax error"),
|
||||
re.compile(r"unknown command"),
|
||||
re.compile(r"user not present")
|
||||
]
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
self._exec_cli_command('terminal length 0')
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||
|
||||
|
||||
|
46
lib/ansible/plugins/terminal/sros.py
Normal file
46
lib/ansible/plugins/terminal/sros.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
#
|
||||
# (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 re
|
||||
import json
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
|
||||
]
|
||||
|
||||
terminal_errors_re = [
|
||||
re.compile(r"^\r\nError:"),
|
||||
]
|
||||
|
||||
supports_multiplexing = False
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
self._exec_cli_command('environment no more')
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
47
lib/ansible/plugins/terminal/vyos.py
Normal file
47
lib/ansible/plugins/terminal/vyos.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# (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 re
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_prompts_re = [
|
||||
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
||||
re.compile(r"\@[\w\-\.]+:\S+?[>#\$] ?$")
|
||||
]
|
||||
|
||||
terminal_errors_re = [
|
||||
re.compile(r"\n\s*Invalid command:"),
|
||||
re.compile(r"\nCommit failed"),
|
||||
re.compile(r"\n\s+Set failed"),
|
||||
]
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
self._exec_cli_command('set terminal length 0')
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue